Skip to content

Commit 59aa7a2

Browse files
authored
Merge pull request #5158 from markcowl/kvtoken
Add KeyVault Token Capability for AccessToken auth
2 parents 06b2476 + c624199 commit 59aa7a2

File tree

13 files changed

+187
-22
lines changed

13 files changed

+187
-22
lines changed

src/Common/Commands.Common.Authentication.Test/AuthenticationFactoryTests.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
using Microsoft.WindowsAzure.Commands.ScenarioTest;
2222
using Xunit;
2323
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
24+
using System.Linq;
2425

2526
namespace Common.Authentication.Test
2627
{
@@ -98,5 +99,55 @@ public void VerifyValidateAuthorityFalseForOnPremise()
9899

99100
Assert.False(((MockAccessTokenProvider)authFactory.TokenProvider).AdalConfiguration.ValidateAuthority);
100101
}
102+
103+
[Fact]
104+
[Trait(Category.AcceptanceType, Category.CheckIn)]
105+
public void CanAuthenticateWithAccessToken()
106+
{
107+
AzureSessionInitializer.InitializeAzureSession();
108+
string tenant = Guid.NewGuid().ToString();
109+
string userId = "[email protected]";
110+
var armToken = Guid.NewGuid().ToString();
111+
var graphToken = Guid.NewGuid().ToString();
112+
var kvToken = Guid.NewGuid().ToString();
113+
var account = new AzureAccount
114+
{
115+
Id = userId,
116+
Type = AzureAccount.AccountType.AccessToken
117+
};
118+
account.SetTenants(tenant);
119+
account.SetAccessToken(armToken);
120+
account.SetProperty(AzureAccount.Property.GraphAccessToken, graphToken);
121+
account.SetProperty(AzureAccount.Property.KeyVaultAccessToken, kvToken);
122+
var authFactory = new AuthenticationFactory();
123+
var environment = AzureEnvironment.PublicEnvironments.Values.First();
124+
var checkArmToken = authFactory.Authenticate(account, environment, tenant, new System.Security.SecureString(), "Never", null);
125+
VerifyToken(checkArmToken, armToken, userId, tenant);
126+
checkArmToken = authFactory.Authenticate(account, environment, tenant, new System.Security.SecureString(), "Never", null, environment.ActiveDirectoryServiceEndpointResourceId);
127+
VerifyToken(checkArmToken, armToken, userId, tenant);
128+
var checkGraphToken = authFactory.Authenticate(account, environment, tenant, new System.Security.SecureString(), "Never", null, AzureEnvironment.Endpoint.GraphEndpointResourceId);
129+
VerifyToken(checkGraphToken, graphToken, userId, tenant);
130+
checkGraphToken = authFactory.Authenticate(account, environment, tenant, new System.Security.SecureString(), "Never", null, environment.GraphEndpointResourceId);
131+
VerifyToken(checkGraphToken, graphToken, userId, tenant);
132+
var checkKVToken = authFactory.Authenticate(account, environment, tenant, new System.Security.SecureString(), "Never", null, environment.AzureKeyVaultServiceEndpointResourceId);
133+
VerifyToken(checkKVToken, kvToken, userId, tenant);
134+
checkKVToken = authFactory.Authenticate(account, environment, tenant, new System.Security.SecureString(), "Never", null, AzureEnvironment.Endpoint.AzureKeyVaultServiceEndpointResourceId);
135+
VerifyToken(checkKVToken, kvToken, userId, tenant);
136+
}
137+
138+
void VerifyToken(IAccessToken checkToken, string expectedAccessToken, string expectedUserId, string expectedTenant)
139+
{
140+
141+
Assert.True(checkToken is RawAccessToken);
142+
Assert.Equal(expectedAccessToken, checkToken.AccessToken);
143+
Assert.Equal(expectedUserId, checkToken.UserId);
144+
Assert.Equal(expectedTenant, checkToken.TenantId);
145+
checkToken.AuthorizeRequest((type, token) =>
146+
{
147+
Assert.Equal(expectedAccessToken, token);
148+
Assert.Equal("Bearer", type);
149+
});
150+
}
151+
101152
}
102153
}

src/Common/Commands.Common.Authentication.Test/Commands.Common.Authentication.Test.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3-
<Import Project="..\..\..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" />
4-
<Import Project="..\..\..\packages\xunit.core.2.1.0\build\portable-net45+win8+wp8+wpa81\xunit.core.props" Condition="Exists('..\..\..\packages\xunit.core.2.1.0\build\portable-net45+win8+wp8+wpa81\xunit.core.props')" />
3+
<Import Project="..\..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props" Condition="Exists('..\..\packages\xunit.runner.visualstudio.2.1.0\build\net20\xunit.runner.visualstudio.props')" />
4+
<Import Project="..\..\packages\xunit.core.2.1.0\build\portable-net45+win8+wp8+wpa81\xunit.core.props" Condition="Exists('..\..\packages\xunit.core.2.1.0\build\portable-net45+win8+wp8+wpa81\xunit.core.props')" />
55
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
66
<PropertyGroup>
77
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

src/Common/Commands.Common.Authentication.Test/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,5 +20,5 @@
2020
<package id="xunit.core" version="2.1.0" targetFramework="net45" />
2121
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net45" />
2222
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net45" />
23-
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
24-
</packages>
23+
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" developmentDependency="true" />
24+
</packages>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
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 System;
16+
17+
namespace Microsoft.Azure.Commands.Common.Authentication
18+
{
19+
public class RawAccessToken : IAccessToken
20+
{
21+
public string AccessToken
22+
{
23+
get; set;
24+
}
25+
26+
public string LoginType
27+
{
28+
get; set;
29+
}
30+
31+
public string TenantId
32+
{
33+
get; set;
34+
}
35+
36+
public string UserId
37+
{
38+
get; set;
39+
}
40+
41+
public void AuthorizeRequest(Action<string, string> authTokenSetter)
42+
{
43+
authTokenSetter("Bearer", AccessToken);
44+
}
45+
}
46+
}

src/Common/Commands.Common.Authentication/Commands.Common.Authentication.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@
133133
<Compile Include="Authentication\ITokenProvider.cs" />
134134
<Compile Include="Authentication\KeyStoreApplicationCredentialProvider.cs" />
135135
<Compile Include="Authentication\ProtectedFileTokenCache.cs" />
136+
<Compile Include="Authentication\RawAccessToken.cs" />
136137
<Compile Include="Authentication\ServicePrincipalKeyStore.cs" />
137138
<Compile Include="Authentication\ServicePrincipalTokenProvider.cs" />
138139
<Compile Include="Authentication\UserTokenProvider.cs" />

src/Common/Commands.Common.Authentication/Factories/AuthenticationFactory.cs

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,42 @@ public IAccessToken Authenticate(
6262
configuration.ClientRedirectUri,
6363
configuration.ResourceClientUri,
6464
configuration.ValidateAuthority);
65-
if (account.IsPropertySet(AzureAccount.Property.CertificateThumbprint))
65+
if (account != null && environment != null
66+
&& account.Type == AzureAccount.AccountType.AccessToken)
67+
{
68+
var rawToken = new RawAccessToken
69+
{
70+
TenantId = tenant,
71+
UserId = account.Id,
72+
LoginType = AzureAccount.AccountType.AccessToken
73+
};
74+
75+
if ((string.Equals(resourceId, environment.AzureKeyVaultServiceEndpointResourceId, StringComparison.OrdinalIgnoreCase)
76+
|| string.Equals(AzureEnvironment.Endpoint.AzureKeyVaultServiceEndpointResourceId, resourceId, StringComparison.OrdinalIgnoreCase))
77+
&& account.IsPropertySet(AzureAccount.Property.KeyVaultAccessToken))
78+
{
79+
rawToken.AccessToken = account.GetProperty(AzureAccount.Property.KeyVaultAccessToken);
80+
}
81+
else if ((string.Equals(resourceId, environment.GraphEndpointResourceId, StringComparison.OrdinalIgnoreCase)
82+
|| string.Equals(AzureEnvironment.Endpoint.GraphEndpointResourceId, resourceId, StringComparison.OrdinalIgnoreCase))
83+
&& account.IsPropertySet(AzureAccount.Property.GraphAccessToken))
84+
{
85+
rawToken.AccessToken = account.GetProperty(AzureAccount.Property.GraphAccessToken);
86+
}
87+
else if ((string.Equals(resourceId, environment.ActiveDirectoryServiceEndpointResourceId, StringComparison.OrdinalIgnoreCase)
88+
|| string.Equals(AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId, resourceId, StringComparison.OrdinalIgnoreCase))
89+
&& account.IsPropertySet(AzureAccount.Property.AccessToken))
90+
{
91+
rawToken.AccessToken = account.GetAccessToken();
92+
}
93+
else
94+
{
95+
throw new InvalidOperationException(string.Format(Resources.AccessTokenResourceNotFound, resourceId));
96+
}
97+
98+
token = rawToken;
99+
}
100+
else if (account.IsPropertySet(AzureAccount.Property.CertificateThumbprint))
66101
{
67102
var thumbprint = account.GetProperty(AzureAccount.Property.CertificateThumbprint);
68103
#if !NETSTANDARD
@@ -325,7 +360,7 @@ private AdalConfiguration GetAdalConfiguration(IAzureEnvironment environment, st
325360
string.Format("No Active Directory endpoint specified for environment '{0}'", environment.Name));
326361
}
327362

328-
var audience = environment.GetEndpoint(resourceId);
363+
var audience = environment.GetEndpoint(resourceId)?? resourceId;
329364
if (string.IsNullOrWhiteSpace(audience))
330365
{
331366
string message = Resources.InvalidManagementTokenAudience;

src/Common/Commands.Common.Authentication/Properties/Resources.Designer.cs

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Common/Commands.Common.Authentication/Properties/Resources.resx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,4 +310,8 @@
310310
<value>[Common.Authentication]: Parsed token '{0}' with json value '{1}' and decoded issuer '{2}'.</value>
311311
<comment>0 = raw token; 1 = decoded token; 2 = issuer</comment>
312312
</data>
313+
<data name="AccessTokenResourceNotFound" xml:space="preserve">
314+
<value>Cannot retrieve access token for resource '{0}'. Please ensure that you have provided the appropriate access tokens when using access token login.</value>
315+
<comment>{0} = authentication resource id</comment>
316+
</data>
313317
</root>

src/Common/Commands.ScenarioTests.Common/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,5 @@
2121
<package id="xunit.core" version="2.1.0" targetFramework="net45" />
2222
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net45" />
2323
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net45" />
24-
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
25-
</packages>
24+
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" developmentDependency="true" />
25+
</packages>

src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,5 @@
2323
<package id="xunit.core" version="2.1.0" targetFramework="net45" />
2424
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net45" />
2525
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net45" />
26-
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
27-
</packages>
26+
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" developmentDependency="true" />
27+
</packages>

src/ResourceManager/KeyVault/Commands.KeyVault/Models/DataServiceCredential.cs

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,21 +74,23 @@ public string GetToken()
7474

7575
private static string GetTenantId(IAzureContext context)
7676
{
77-
var tenantId = string.Empty;
7877
if (context.Account == null)
78+
{
7979
throw new ArgumentException(KeyVaultProperties.Resources.ArmAccountNotFound);
80+
}
8081

81-
if (context.Account.Type != AzureAccount.AccountType.User &&
82-
context.Account.Type != AzureAccount.AccountType.ServicePrincipal)
83-
throw new ArgumentException(string.Format(KeyVaultProperties.Resources.UnsupportedAccountType, context.Account.Type));
84-
85-
if (context.Subscription != null && context.Account != null)
82+
var tenantId = string.Empty;
83+
if (context.Tenant != null && context.Tenant.GetId() != Guid.Empty)
84+
{
85+
tenantId = context.Tenant.Id.ToString();
86+
}
87+
else if (string.IsNullOrWhiteSpace(tenantId) && context.Subscription != null && context.Account != null)
88+
{
8689
tenantId = context.Subscription.GetPropertyAsArray(AzureSubscription.Property.Tenants)
8790
.Intersect(context.Account.GetPropertyAsArray(AzureAccount.Property.Tenants))
8891
.FirstOrDefault();
92+
}
8993

90-
if (string.IsNullOrWhiteSpace(tenantId) && context.Tenant != null && context.Tenant.GetId() != Guid.Empty)
91-
tenantId = context.Tenant.Id.ToString();
9294
return tenantId;
9395
}
9496

src/ResourceManager/Profile/Commands.Profile.Test/LongRunningCmdletTests.cs

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
using Microsoft.Azure.Commands.Common;
1616
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
17+
using Microsoft.Azure.Commands.ScenarioTest;
18+
using Microsoft.Azure.ServiceManagemenet.Common.Models;
1719
using Microsoft.WindowsAzure.Commands.ScenarioTest;
1820
using Microsoft.WindowsAzure.Commands.Test.Utilities.Common;
1921
using Microsoft.WindowsAzure.Commands.Utilities.Common;
@@ -24,6 +26,7 @@
2426
using System.Management.Automation;
2527
using System.Threading;
2628
using Xunit;
29+
using Xunit.Abstractions;
2730

2831
namespace Microsoft.Azure.Commands.Profile.Test
2932
{
@@ -33,8 +36,16 @@ public class LongRunningCmdletTests : RMTestBase
3336
static readonly ProgressRecord Progress = new ProgressRecord(0, "activity", "description");
3437
static readonly ErrorRecord Error = new ErrorRecord(new InvalidOperationException("invalid operation"), "12345", ErrorCategory.InvalidOperation, new object());
3538
static readonly object Output = new TestCmdletOutput();
39+
static readonly object lockObject = new object();
3640

3741
ManualResetEvent jobCompleted = new ManualResetEvent(false);
42+
private XunitTracingInterceptor xunitLogger;
43+
44+
public LongRunningCmdletTests(ITestOutputHelper output)
45+
{
46+
TestExecutionHelpers.SetUpSessionAndProfile();
47+
xunitLogger = new XunitTracingInterceptor(output);
48+
}
3849

3950
[Fact]
4051
[Trait(Category.AcceptanceType, Category.CheckIn)]
@@ -56,7 +67,6 @@ public void CanSupportShouldProcess()
5667
Mock<ICommandRuntime> mockRuntime;
5768
var cmdlet = SetupCmdlet(true, false, out mockRuntime);
5869
var job = cmdlet.ExecuteAsJob("Test Job") as AzureLongRunningJob<AzureStreamTestCmdlet>;
59-
job.StateChanged += this.HandleStateChange;
6070
WaitForCompletion(job, j =>
6171
{
6272
ValidateCompletedCmdlet(job);
@@ -226,9 +236,10 @@ AzureStreamTestCmdlet SetupCmdlet(bool CallShouldProcess, bool CallShouldContinu
226236

227237
public void HandleStateChange(object sender, JobStateEventArgs args)
228238
{
229-
lock (this)
239+
lock (lockObject)
230240
{
231241
var job = sender as AzureLongRunningJob;
242+
xunitLogger.Information(string.Format("[statechangedhandler]: previous state: '{0}', current state: '{1}'", args.PreviousJobStateInfo?.State, args.JobStateInfo?.State));
232243
if (args.JobStateInfo.State == JobState.Completed || args.JobStateInfo.State == JobState.Failed || args.JobStateInfo.State == JobState.Stopped)
233244
{
234245
this.jobCompleted.Set();
@@ -254,7 +265,8 @@ void WaitForCompletion(AzureLongRunningJob job, Action<AzureLongRunningJob> vali
254265
job.StateChanged += this.HandleStateChange;
255266
try
256267
{
257-
if (this.jobCompleted.WaitOne(TimeSpan.FromSeconds(10)))
268+
HandleStateChange(job, new JobStateEventArgs(job.JobStateInfo, new JobStateInfo(JobState.NotStarted)));
269+
if (this.jobCompleted.WaitOne(TimeSpan.FromSeconds(30)))
258270
{
259271
validate(job);
260272
}
@@ -266,6 +278,10 @@ void WaitForCompletion(AzureLongRunningJob job, Action<AzureLongRunningJob> vali
266278
finally
267279
{
268280
job.StateChanged -= this.HandleStateChange;
281+
foreach (var message in job.Debug)
282+
{
283+
xunitLogger.Information(message?.Message);
284+
}
269285
}
270286

271287
}
@@ -287,6 +303,7 @@ public class TestCmdletOutput
287303
public string Property { get { return "PropertyValue"; } }
288304
}
289305

306+
[Cmdlet(VerbsDiagnostic.Test, "AzureJob",ConfirmImpact =ConfirmImpact.High)]
290307
public class AzureStreamTestCmdlet : AzurePSCmdlet
291308
{
292309
public IList<ErrorRecord> Error { get; set; } = new List<ErrorRecord>();

src/ResourceManager/Profile/Commands.Profile.Test/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,5 @@
3232
<package id="xunit.core" version="2.1.0" targetFramework="net45" />
3333
<package id="xunit.extensibility.core" version="2.1.0" targetFramework="net45" />
3434
<package id="xunit.extensibility.execution" version="2.1.0" targetFramework="net45" />
35-
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" />
35+
<package id="xunit.runner.visualstudio" version="2.1.0" targetFramework="net45" developmentDependency="true" />
3636
</packages>

0 commit comments

Comments
 (0)