diff --git a/AzurePowershell.Test.targets b/AzurePowershell.Test.targets index 2b624326c503..a4adcf2014e1 100644 --- a/AzurePowershell.Test.targets +++ b/AzurePowershell.Test.targets @@ -84,7 +84,8 @@ - + + diff --git a/src/Common/Commands.Common.Authentication.Test/AuthenticationFactoryTests.cs b/src/Common/Commands.Common.Authentication.Test/AuthenticationFactoryTests.cs new file mode 100644 index 000000000000..7f2afce7000d --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/AuthenticationFactoryTests.cs @@ -0,0 +1,111 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Factories; +using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.WindowsAzure.Commands.Test.Utilities.Common; +using System; +using System.Collections.Generic; +using Microsoft.WindowsAzure.Commands.ScenarioTest; +using Xunit; + +namespace Common.Authentication.Test +{ + public class AuthenticationFactoryTests + { + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void VerifySubscriptionTokenCacheRemove() + { + var authFactory = new AuthenticationFactory + { + TokenProvider = new MockAccessTokenProvider("testtoken", "testuser") + }; + + var subscriptionId = Guid.NewGuid(); + + var credential = authFactory.GetSubscriptionCloudCredentials(new AzureContext + ( + new AzureSubscription + { + Id = subscriptionId, + Properties = new Dictionary + { + { AzureSubscription.Property.Tenants, "123"} + } + }, + new AzureAccount + { + Id = "testuser", + Type = AzureAccount.AccountType.User, + Properties = new Dictionary + { + { AzureAccount.Property.Tenants, "123" } + } + }, + AzureEnvironment.PublicEnvironments["AzureCloud"] + )); + + Assert.True(credential is AccessTokenCredential); + Assert.Equal(subscriptionId, new Guid(((AccessTokenCredential)credential).SubscriptionId)); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void VerifyValidateAuthorityFalseForOnPremise() + { + var authFactory = new AuthenticationFactory + { + TokenProvider = new MockAccessTokenProvider("testtoken", "testuser") + }; + + var subscriptionId = Guid.NewGuid(); + var context = new AzureContext + ( + new AzureSubscription + { + Id = subscriptionId, + Properties = new Dictionary + { + { AzureSubscription.Property.Tenants, "123"} + } + }, + new AzureAccount + { + Id = "testuser", + Type = AzureAccount.AccountType.User, + Properties = new Dictionary + { + { AzureAccount.Property.Tenants, "123" } + } + }, + new AzureEnvironment + { + Name = "Katal", + OnPremise = true, + Endpoints = new Dictionary + { + { AzureEnvironment.Endpoint.ActiveDirectory, "http://ad.com" }, + { AzureEnvironment.Endpoint.ActiveDirectoryServiceEndpointResourceId, "http://adresource.com" } + } + } + ); + + var credential = authFactory.Authenticate(context.Account, context.Environment, "common", null, ShowDialog.Always); + + Assert.False(((MockAccessTokenProvider)authFactory.TokenProvider).AdalConfiguration.ValidateAuthority); + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/AzureRMProfileTests.cs b/src/Common/Commands.Common.Authentication.Test/AzureRMProfileTests.cs new file mode 100644 index 000000000000..49cf5fdcd40c --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/AzureRMProfileTests.cs @@ -0,0 +1,230 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.WindowsAzure.Commands.Common.Test.Mocks; +using System; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using Microsoft.WindowsAzure.Commands.ScenarioTest; +using Xunit; + +namespace Common.Authentication.Test +{ + public class AzureRMProfileTests + { + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void ProfileSerializeDeserializeWorks() + { + var dataStore = new MockDataStore(); + AzureSession.DataStore = dataStore; + var profilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AzureSession.ProfileFile); + var currentProfile = new AzureRMProfile(profilePath); + var tenantId = Guid.NewGuid().ToString(); + var environment = new AzureEnvironment + { + Name = "testCloud", + Endpoints = { { AzureEnvironment.Endpoint.ActiveDirectory, "http://contoso.com" } } + }; + var account = new AzureAccount + { + Id = "me@contoso.com", + Type = AzureAccount.AccountType.User, + Properties = { { AzureAccount.Property.Tenants, tenantId } } + }; + var sub = new AzureSubscription + { + Account = account.Id, + Environment = environment.Name, + Id = new Guid(), + Name = "Contoso Test Subscription", + Properties = { { AzureSubscription.Property.Tenants, tenantId } } + }; + var tenant = new AzureTenant + { + Id = new Guid(tenantId), + Domain = "contoso.com" + }; + + currentProfile.Context = new AzureContext(sub, account, environment, tenant); + currentProfile.Environments[environment.Name] = environment; + currentProfile.Context.TokenCache = new byte[] { 1, 2, 3, 4, 5, 6, 8, 9, 0 }; + + AzureRMProfile deserializedProfile; + // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter + BinaryFormatter bf = new BinaryFormatter(); + using (MemoryStream ms = new MemoryStream()) + { + // "Save" object state + bf.Serialize(ms, currentProfile); + + // Re-use the same stream for de-serialization + ms.Seek(0, 0); + + // Replace the original exception with de-serialized one + deserializedProfile = (AzureRMProfile)bf.Deserialize(ms); + } + Assert.NotNull(deserializedProfile); + var jCurrentProfile = currentProfile.ToString(); + var jDeserializedProfile = deserializedProfile.ToString(); + Assert.Equal(jCurrentProfile, jDeserializedProfile); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void SavingProfileWorks() + { + string expected = @"{ + ""Environments"": { + ""testCloud"": { + ""Name"": ""testCloud"", + ""OnPremise"": false, + ""Endpoints"": { + ""ActiveDirectory"": ""http://contoso.com"" + } + } + }, + ""Context"": { + ""Account"": { + ""Id"": ""me@contoso.com"", + ""Type"": 1, + ""Properties"": { + ""Tenants"": ""3c0ff8a7-e8bb-40e8-ae66-271343379af6"" + } + }, + ""Subscription"": { + ""Id"": ""00000000-0000-0000-0000-000000000000"", + ""Name"": ""Contoso Test Subscription"", + ""Environment"": ""testCloud"", + ""Account"": ""me@contoso.com"", + ""State"": ""Enabled"", + ""Properties"": { + ""Tenants"": ""3c0ff8a7-e8bb-40e8-ae66-271343379af6"" + } + }, + ""Environment"": { + ""Name"": ""testCloud"", + ""OnPremise"": false, + ""Endpoints"": { + ""ActiveDirectory"": ""http://contoso.com"" + } + }, + ""Tenant"": { + ""Id"": ""3c0ff8a7-e8bb-40e8-ae66-271343379af6"", + ""Domain"": ""contoso.com"" + }, + ""TokenCache"": ""AQIDBAUGCAkA"" + } +}"; + var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AzureSession.ProfileFile); + var dataStore = new MockDataStore(); + AzureSession.DataStore = dataStore; + AzureRMProfile profile = new AzureRMProfile(path); + var tenantId = new Guid("3c0ff8a7-e8bb-40e8-ae66-271343379af6"); + var environment = new AzureEnvironment + { + Name = "testCloud", + Endpoints = { { AzureEnvironment.Endpoint.ActiveDirectory, "http://contoso.com" } } + }; + var account = new AzureAccount + { + Id = "me@contoso.com", + Type = AzureAccount.AccountType.User, + Properties = { { AzureAccount.Property.Tenants, tenantId.ToString() } } + }; + var sub = new AzureSubscription + { + Account = account.Id, + Environment = environment.Name, + Id = new Guid(), + Name = "Contoso Test Subscription", + State = "Enabled", + Properties = { { AzureSubscription.Property.Tenants, tenantId.ToString() } } + }; + var tenant = new AzureTenant + { + Id = tenantId, + Domain = "contoso.com" + }; + profile.Context = new AzureContext(sub, account, environment, tenant); + profile.Environments[environment.Name] = environment; + profile.Context.TokenCache = new byte[] { 1, 2, 3, 4, 5, 6, 8, 9, 0 }; + profile.Save(); + string actual = dataStore.ReadFileAsText(path); + Assert.Equal(expected, actual); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void LoadingProfileWorks() + { + string contents = @"{ + ""Environments"": { + ""testCloud"": { + ""Name"": ""testCloud"", + ""OnPremise"": false, + ""Endpoints"": { + ""ActiveDirectory"": ""http://contoso.com"" + } + } + }, + ""Context"": { + ""TokenCache"": ""AQIDBAUGCAkA"", + ""Account"": { + ""Id"": ""me@contoso.com"", + ""Type"": 1, + ""Properties"": { + ""Tenants"": ""3c0ff8a7-e8bb-40e8-ae66-271343379af6"" + } + }, + ""Subscription"": { + ""Id"": ""00000000-0000-0000-0000-000000000000"", + ""Name"": ""Contoso Test Subscription"", + ""Environment"": ""testCloud"", + ""Account"": ""me@contoso.com"", + ""Properties"": { + ""Tenants"": ""3c0ff8a7-e8bb-40e8-ae66-271343379af6"" + } + }, + ""Environment"": { + ""Name"": ""testCloud"", + ""OnPremise"": false, + ""Endpoints"": { + ""ActiveDirectory"": ""http://contoso.com"" + } + }, + ""Tenant"": { + ""Id"": ""3c0ff8a7-e8bb-40e8-ae66-271343379af6"", + ""Domain"": ""contoso.com"" + } + } +}"; + var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AzureSession.ProfileFile); + var dataStore = new MockDataStore(); + AzureSession.DataStore = dataStore; + dataStore.WriteFile(path, contents); + var profile = new AzureRMProfile(path); + Assert.Equal(4, profile.Environments.Count); + Assert.Equal("3c0ff8a7-e8bb-40e8-ae66-271343379af6", profile.Context.Tenant.Id.ToString()); + Assert.Equal("contoso.com", profile.Context.Tenant.Domain); + Assert.Equal("00000000-0000-0000-0000-000000000000", profile.Context.Subscription.Id.ToString()); + Assert.Equal("testCloud", profile.Context.Environment.Name); + Assert.Equal("me@contoso.com", profile.Context.Account.Id); + Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 6, 8, 9, 0 }, profile.Context.TokenCache); + Assert.Equal(path, profile.ProfilePath); + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/AzureSMProfileTests.cs b/src/Common/Commands.Common.Authentication.Test/AzureSMProfileTests.cs new file mode 100644 index 000000000000..663b83eb21db --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/AzureSMProfileTests.cs @@ -0,0 +1,193 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.WindowsAzure.Commands.Common.Test.Mocks; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization.Formatters.Binary; +using Microsoft.WindowsAzure.Commands.ScenarioTest; +using Xunit; + +namespace Common.Authentication.Test +{ + public class AzureSMProfileTests + { + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void ProfileSaveDoesNotSerializeContext() + { + var dataStore = new MockDataStore(); + var profilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AzureSession.ProfileFile); + var profile = new AzureSMProfile(profilePath); + AzureSession.DataStore = dataStore; + var tenant = Guid.NewGuid().ToString(); + var environment = new AzureEnvironment + { + Name = "testCloud", + Endpoints = { { AzureEnvironment.Endpoint.ActiveDirectory, "http://contoso.com" } } + }; + var account = new AzureAccount + { + Id = "me@contoso.com", + Type = AzureAccount.AccountType.User, + Properties = { { AzureAccount.Property.Tenants, tenant } } + }; + var sub = new AzureSubscription + { + Account = account.Id, + Environment = environment.Name, + Id = new Guid(), + Name = "Contoso Test Subscription", + Properties = { { AzureSubscription.Property.Tenants, tenant } } + }; + + profile.Environments[environment.Name] = environment; + profile.Accounts[account.Id] = account; + profile.Subscriptions[sub.Id] = sub; + + profile.Save(); + + var profileFile = profile.ProfilePath; + string profileContents = dataStore.ReadFileAsText(profileFile); + var readProfile = JsonConvert.DeserializeObject>(profileContents); + Assert.False(readProfile.ContainsKey("DefaultContext")); + AzureSMProfile parsedProfile = new AzureSMProfile(); + var serializer = new JsonProfileSerializer(); + Assert.True(serializer.Deserialize(profileContents, parsedProfile)); + Assert.NotNull(parsedProfile); + Assert.NotNull(parsedProfile.Environments); + Assert.True(parsedProfile.Environments.ContainsKey(environment.Name)); + Assert.NotNull(parsedProfile.Accounts); + Assert.True(parsedProfile.Accounts.ContainsKey(account.Id)); + Assert.NotNull(parsedProfile.Subscriptions); + Assert.True(parsedProfile.Subscriptions.ContainsKey(sub.Id)); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void ProfileSerializeDeserializeWorks() + { + var dataStore = new MockDataStore(); + var profilePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, AzureSession.ProfileFile); + var profile = new AzureSMProfile(profilePath); + AzureSession.DataStore = dataStore; + var tenant = Guid.NewGuid().ToString(); + var environment = new AzureEnvironment + { + Name = "testCloud", + Endpoints = { { AzureEnvironment.Endpoint.ActiveDirectory, "http://contoso.com" } } + }; + var account = new AzureAccount + { + Id = "me@contoso.com", + Type = AzureAccount.AccountType.User, + Properties = { { AzureAccount.Property.Tenants, tenant } } + }; + var sub = new AzureSubscription + { + Account = account.Id, + Environment = environment.Name, + Id = new Guid(), + Name = "Contoso Test Subscription", + Properties = { { AzureSubscription.Property.Tenants, tenant } } + }; + + profile.Environments[environment.Name] = environment; + profile.Accounts[account.Id] = account; + profile.Subscriptions[sub.Id] = sub; + + AzureSMProfile deserializedProfile; + // Round-trip the exception: Serialize and de-serialize with a BinaryFormatter + BinaryFormatter bf = new BinaryFormatter(); + using (MemoryStream ms = new MemoryStream()) + { + // "Save" object state + bf.Serialize(ms, profile); + + // Re-use the same stream for de-serialization + ms.Seek(0, 0); + + // Replace the original exception with de-serialized one + deserializedProfile = (AzureSMProfile)bf.Deserialize(ms); + } + Assert.NotNull(deserializedProfile); + var jCurrentProfile = JsonConvert.SerializeObject(profile); + var jDeserializedProfile = JsonConvert.SerializeObject(deserializedProfile); + Assert.Equal(jCurrentProfile, jDeserializedProfile); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void AccountMatchingIgnoresCase() + { + var profile = new AzureSMProfile(); + string accountName = "howdy@contoso.com"; + string accountNameCase = "Howdy@Contoso.com"; + var subscriptionId = Guid.NewGuid(); + var tenantId = Guid.NewGuid(); + var account = new AzureAccount + { + Id = accountName, + Type = AzureAccount.AccountType.User + }; + + account.SetProperty(AzureAccount.Property.Subscriptions, subscriptionId.ToString()); + account.SetProperty(AzureAccount.Property.Tenants, tenantId.ToString()); + var subscription = new AzureSubscription + { + Id = subscriptionId, + Account = accountNameCase, + Environment = EnvironmentName.AzureCloud + }; + + subscription.SetProperty(AzureSubscription.Property.Default, "true"); + subscription.SetProperty(AzureSubscription.Property.Tenants, tenantId.ToString()); + profile.Accounts.Add(accountName, account); + profile.Subscriptions.Add(subscriptionId, subscription); + Assert.NotNull(profile.Context); + Assert.NotNull(profile.Context.Account); + Assert.NotNull(profile.Context.Environment); + Assert.NotNull(profile.Context.Subscription); + Assert.Equal(account, profile.Context.Account); + Assert.Equal(subscription, profile.Context.Subscription); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void GetsCorrectContext() + { + AzureSMProfile profile = new AzureSMProfile(); + string accountId = "accountId"; + Guid subscriptionId = Guid.NewGuid(); + profile.Accounts.Add(accountId, new AzureAccount { Id = accountId, Type = AzureAccount.AccountType.User }); + profile.Subscriptions.Add(subscriptionId, new AzureSubscription + { + Account = accountId, + Environment = EnvironmentName.AzureChinaCloud, + Name = "hello", + Id = subscriptionId + }); + profile.DefaultSubscription = profile.Subscriptions[subscriptionId]; + AzureContext context = profile.Context; + + Assert.Equal(accountId, context.Account.Id); + Assert.Equal(subscriptionId, context.Subscription.Id); + Assert.Equal(EnvironmentName.AzureChinaCloud, context.Environment.Name); + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/ClientFactoryHandlerTests.cs b/src/Common/Commands.Common.Authentication.Test/ClientFactoryHandlerTests.cs new file mode 100644 index 000000000000..9b838cd9e9e5 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/ClientFactoryHandlerTests.cs @@ -0,0 +1,78 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.WindowsAzure.Management.Storage; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Security; +using Microsoft.Azure.Commands.Common.Authentication.Factories; +using Microsoft.WindowsAzure.Commands.Common.Test.Mocks; +using Microsoft.WindowsAzure.Commands.ScenarioTest; +using Xunit; + +namespace Common.Authentication.Test +{ + public class ClientFactoryHandlerTests + { + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void DelegatingHandlersAreCloned() + { + string userAccount = "user@contoso.com"; + Guid subscriptionId = Guid.NewGuid(); + AzureContext context = new AzureContext + ( + new AzureSubscription() + { + Account = userAccount, + Environment = "AzureCloud", + Id = subscriptionId, + Properties = new Dictionary() { { AzureSubscription.Property.Tenants, "common" } } + }, + new AzureAccount() + { + Id = userAccount, + Type = AzureAccount.AccountType.User, + Properties = new Dictionary() { { AzureAccount.Property.Tenants, "common" } } + }, + AzureEnvironment.PublicEnvironments["AzureCloud"] + ); + + AzureSession.AuthenticationFactory = new MockTokenAuthenticationFactory(userAccount, Guid.NewGuid().ToString()); + var mockHandler = new MockDelegatingHandler(); + var factory = new ClientFactory(); + factory.AddHandler(mockHandler); + var client = factory.CreateClient(context, AzureEnvironment.Endpoint.ServiceManagement); + client = factory.CreateClient(context, AzureEnvironment.Endpoint.ServiceManagement); + client = factory.CreateClient(context, AzureEnvironment.Endpoint.ServiceManagement); + client = factory.CreateClient(context, AzureEnvironment.Endpoint.ServiceManagement); + client = factory.CreateClient(context, AzureEnvironment.Endpoint.ServiceManagement); + Assert.Equal(5, MockDelegatingHandler.cloneCount); + } + + private class MockDelegatingHandler : DelegatingHandler, ICloneable + { + public static int cloneCount = 0; + + public object Clone() + { + cloneCount++; + return this; + } + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/ClientFactoryTests.cs b/src/Common/Commands.Common.Authentication.Test/ClientFactoryTests.cs new file mode 100644 index 000000000000..4ea2bfdeec8f --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/ClientFactoryTests.cs @@ -0,0 +1,145 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using Microsoft.Azure.Commands.Common.Authentication.Models; +using Microsoft.WindowsAzure.Management.Storage; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Security; +using Microsoft.Azure.Commands.Common.Authentication.Factories; +using Microsoft.WindowsAzure.Commands.Common.Test.Mocks; +using Microsoft.WindowsAzure.Commands.ScenarioTest; +using Xunit; + +namespace Common.Authentication.Test +{ + public class ClientFactoryTests : IDisposable + { + private string subscriptionId; + + private string userAccount; + + private SecureString password; + + private bool runTest; + + public ClientFactoryTests() + { + // Example of environment variable: TEST_AZURE_CREDENTIALS=;;" + string credsEnvironmentVariable = Environment.GetEnvironmentVariable("TEST_AZURE_CREDENTIALS") ?? ""; + string[] creds = credsEnvironmentVariable.Split(';'); + + if (creds.Length != 3) + { + // The test is not configured to run. + runTest = false; + return; + } + + subscriptionId = creds[0]; + userAccount = creds[1]; + password = new SecureString(); + foreach (char letter in creds[2]) + { + password.AppendChar(letter); + } + password = password.Length == 0 ? null : password; + runTest = true; + } + + /// + /// This test run live against Azure to list storage accounts under current subscription. + /// + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void VerifyClientFactoryWorks() + { + if (!runTest) + { + return; + } + + AzureContext context = new AzureContext + ( + new AzureSubscription() + { + Account = userAccount, + Environment = "AzureCloud", + Id = Guid.Parse(subscriptionId), + Properties = new Dictionary() { { AzureSubscription.Property.Tenants, "common" } } + }, + new AzureAccount() + { + Id = userAccount, + Type = AzureAccount.AccountType.User, + Properties = new Dictionary() { { AzureAccount.Property.Tenants, "common" } } + }, + AzureEnvironment.PublicEnvironments["AzureCloud"] + ); + + // Add registration action to make sure we register for the used provider (if required) + // AzureSession.ClientFactory.AddAction(new RPRegistrationAction()); + + // Authenticate! + AzureSession.AuthenticationFactory.Authenticate(context.Account, context.Environment, "common", password, ShowDialog.Always); + + AzureSession.ClientFactory.AddUserAgent("TestUserAgent", "1.0"); + // Create the client + var client = AzureSession.ClientFactory.CreateClient(context, AzureEnvironment.Endpoint.ServiceManagement); + + // List storage accounts + var storageAccounts = client.StorageAccounts.List().StorageAccounts; + foreach (var storageAccount in storageAccounts) + { + Assert.NotNull(storageAccount); + } + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void VerifyProductInfoHeaderValueEquality() + { + ClientFactory factory = new ClientFactory(); + factory.AddUserAgent("test1", "123"); + factory.AddUserAgent("test2", "123"); + factory.AddUserAgent("test1", "123"); + factory.AddUserAgent("test1", "456"); + factory.AddUserAgent("test3"); + factory.AddUserAgent("tesT3"); + + Assert.Equal(4, factory.UserAgents.Count); + Assert.True(factory.UserAgents.Any(u => u.Product.Name == "test1" && u.Product.Version == "123")); + Assert.True(factory.UserAgents.Any(u => u.Product.Name == "test2" && u.Product.Version == "123")); + Assert.True(factory.UserAgents.Any(u => u.Product.Name == "test1" && u.Product.Version == "456")); + Assert.True(factory.UserAgents.Any(u => u.Product.Name == "test3" && u.Product.Version == null)); + } + + public virtual void Dispose(bool disposing) + { + if (disposing && password != null) + { + password.Dispose(); + password = null; + } + } + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/Commands.Common.Authentication.Test.csproj b/src/Common/Commands.Common.Authentication.Test/Commands.Common.Authentication.Test.csproj new file mode 100644 index 000000000000..763ebd8ca5d5 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Commands.Common.Authentication.Test.csproj @@ -0,0 +1,190 @@ + + + + + + + Debug + AnyCPU + {C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D} + Library + Properties + Microsoft.Azure.Commands.Common.Authentication.Test + Microsoft.Azure.Commands.Common.Authentication.Test + v4.5 + 512 + + ..\..\ + true + 9fdcb6f4 + + + true + full + false + bin\Debug + DEBUG;TRACE + prompt + 4 + true + true + false + + + bin\Release + TRACE;SIGN + true + pdbonly + AnyCPU + bin\Release\Microsoft.Azure.Commands.Profile.Test.dll.CodeAnalysisLog.xml + true + GlobalSuppressions.cs + prompt + MinimumRecommendedRules.ruleset + ;$(ProgramFiles)\Microsoft Visual Studio 12.0\Team Tools\Static Analysis Tools\Rule Sets + ;$(ProgramFiles)\Microsoft Visual Studio 12.0\Team Tools\Static Analysis Tools\FxCop\Rules + true + MSSharedLibKey.snk + true + true + false + + + + ..\..\packages\Hyak.Common.1.0.3\lib\net45\Hyak.Common.dll + True + + + False + ..\..\packages\Microsoft.Azure.Common.2.1.0\lib\net45\Microsoft.Azure.Common.dll + + + False + ..\..\packages\Microsoft.Azure.Common.2.1.0\lib\net45\Microsoft.Azure.Common.NetFramework.dll + + + + ..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.18.206251556\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll + True + + + ..\..\packages\Microsoft.Rest.ClientRuntime.2.0.1\lib\portable-net45+win+wpa81\Microsoft.Rest.ClientRuntime.dll + True + + + ..\..\packages\Microsoft.Rest.ClientRuntime.Azure.3.0.2\lib\net45\Microsoft.Rest.ClientRuntime.Azure.dll + True + + + ..\..\packages\Microsoft.Rest.ClientRuntime.Azure.Authentication.2.0.1-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.Authentication.dll + True + + + ..\..\packages\Microsoft.WindowsAzure.Management.Storage.5.1.1\lib\net40\Microsoft.WindowsAzure.Management.Storage.dll + True + + + ..\..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll + + + ..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll + + + + + + ..\..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Extensions.dll + True + + + ..\..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Primitives.dll + True + + + + + ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll + True + + + ..\..\packages\xunit.assert.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.assert.dll + True + + + ..\..\packages\xunit.extensibility.core.2.1.0\lib\portable-net45+win8+wp8+wpa81\xunit.core.dll + True + + + ..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + True + + + + + + + + + + + + + True + True + Resources.resx + + + + + + {d3804b64-c0d3-48f8-82ec-1f632f833c9e} + Commands.Common.Authentication + + + {5ee72c53-1720-4309-b54b-5fb79703195f} + Commands.Common + + + {c1bda476-a5cc-4394-914d-48b0ec31a710} + Commands.ScenarioTests.Common + + + + + Designer + + + + + + + + + + + + + + + + + + + + + + + + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/ConversionUtilitiesTests.cs b/src/Common/Commands.Common.Authentication.Test/ConversionUtilitiesTests.cs new file mode 100644 index 000000000000..2c18f9d7db82 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/ConversionUtilitiesTests.cs @@ -0,0 +1,124 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using System.Collections.Generic; +using Microsoft.WindowsAzure.Commands.ScenarioTest; +using Xunit; + +namespace Microsoft.WindowsAzure.Commands.Common.Test +{ + public class ConversionUtilitiesTests + { + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void DeserializeJsonWorksForSimpleCases() + { + const string json1 = + @"{ + ""foo1"": ""bar1"", + ""foo2"": ""bar2"", + ""num"": 25, + ""address"": + { + ""streetAddress"": ""123 Main Str"", + ""city"": ""Some City"", + }, + ""list"": + [ + { + ""val1"": ""a"", + ""val2"": ""b"" + }, + { + ""val3"": ""c"", + ""val4"": ""d"" + } + ] + }"; + + Dictionary result; + result = JsonUtilities.DeserializeJson(json1); + Assert.NotNull(result); + Assert.Equal(5, result.Count); + Assert.Equal(2, ((Dictionary)result["address"]).Count); + Assert.Equal(2, ((List)result["list"]).Count); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void DeserializeJsonWorksForEmptyObjects() + { + const string json1 = + @"{ + ""foo1"": ""bar1"", + ""foo2"": ""bar2"", + ""num"": 25, + ""address"": + { }, + ""list"": + [ ] + }"; + + Dictionary result; + result = JsonUtilities.DeserializeJson(json1); + Assert.NotNull(result); + Assert.Equal(5, result.Count); + Assert.Equal(0, ((Dictionary)result["address"]).Count); + Assert.Equal(0, ((List)result["list"]).Count); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void DeserializeJsonAcceptsBadArguments() + { + Dictionary result; + result = JsonUtilities.DeserializeJson(null); + Assert.Null(result); + + result = JsonUtilities.DeserializeJson(string.Empty); + Assert.True(result.Count == 0); + } + + [Fact] + [Trait(Category.AcceptanceType, Category.CheckIn)] + public void DeserializeJsonAcceptsBadJson() + { + const string json1 = + @"{ + ""foo1"": ""bar1"", + ""foo2"": ""bar2"", + ""num"": 25, + ""address"": + { + ""streetAddress"": ""123 Main Str"", + ""city"": ""Some City"", + }, + ""list"": + [ + { + ""val1"": ""a"", + ""val2"": ""b"" + }, + { + ""val3"": ""c"", + ""val4"": ""d"" + }"; + + Dictionary result; + result = JsonUtilities.DeserializeJson(json1); + Assert.Null(result); + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/Mocks/MockDataStore.cs b/src/Common/Commands.Common.Authentication.Test/Mocks/MockDataStore.cs new file mode 100644 index 000000000000..61a148ebbdd0 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Mocks/MockDataStore.cs @@ -0,0 +1,318 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using Microsoft.Azure.Commands.Common.Authentication; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using System.Text.RegularExpressions; + +namespace Microsoft.WindowsAzure.Commands.Common.Test.Mocks +{ + public class MockDataStore : IDataStore + { + private Dictionary virtualStore = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private Dictionary certStore = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private const string FolderKey = "Folder"; + + public Dictionary VirtualStore + { + get { return virtualStore; } + set { virtualStore = value; } + } + + public void WriteFile(string path, string contents) + { + VirtualStore[path] = contents; + } + + public void WriteFile(string path, string contents, Encoding encoding) + { + WriteFile(path, contents); + } + + public void WriteFile(string path, byte[] contents) + { + VirtualStore[path] = Encoding.Default.GetString(contents); + } + + public string ReadFileAsText(string path) + { + if (VirtualStore.ContainsKey(path)) + { + return VirtualStore[path]; + } + else + { + throw new IOException("File not found: " + path); + } + } + + public Stream ReadFileAsStream(string path) + { + if (VirtualStore.ContainsKey(path)) + { + MemoryStream stream = new MemoryStream(); + StreamWriter writer = new StreamWriter(stream); + writer.Write(VirtualStore[path]); + writer.Flush(); + stream.Position = 0; + return stream; + } + else + { + throw new IOException("File not found: " + path); + } + } + + public byte[] ReadFileAsBytes(string path) + { + if (VirtualStore.ContainsKey(path)) + { + return Encoding.Default.GetBytes(VirtualStore[path]); + } + else + { + throw new IOException("File not found: " + path); + } + } + + public void RenameFile(string oldPath, string newPath) + { + if (VirtualStore.ContainsKey(oldPath)) + { + VirtualStore[newPath] = VirtualStore[oldPath]; + VirtualStore.Remove(oldPath); + } + else + { + throw new IOException("File not found: " + oldPath); + } + } + + public void CopyFile(string oldPath, string newPath) + { + if (VirtualStore.ContainsKey(oldPath)) + { + VirtualStore[newPath] = VirtualStore[oldPath]; + } + else + { + throw new IOException("File not found: " + oldPath); + } + } + + public bool FileExists(string path) + { + return VirtualStore.ContainsKey(path); + } + + public void DeleteFile(string path) + { + if (VirtualStore.ContainsKey(path)) + { + VirtualStore.Remove(path); + } + else + { + throw new IOException("File not found: " + path); + } + } + + public void DeleteDirectory(string dir) + { + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(dir)) + { + VirtualStore.Remove(key); + } + } + } + + public void EmptyDirectory(string dirPath) + { + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(dirPath)) + { + VirtualStore.Remove(key); + } + } + } + + public bool DirectoryExists(string path) + { + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(path)) + { + return true; + } + } + return false; + } + + public void CreateDirectory(string path) + { + VirtualStore[path] = FolderKey; + } + + public string[] GetDirectories(string sourceDirName) + { + HashSet dirs = new HashSet(); + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(sourceDirName)) + { + var directoryName = Path.GetDirectoryName(key); + if (!dirs.Contains(directoryName)) + { + dirs.Add(directoryName); + } + } + } + return dirs.ToArray(); + } + + public string[] GetDirectories(string startDirectory, string filePattern, SearchOption options) + { + HashSet dirs = new HashSet(); + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(startDirectory) && Regex.IsMatch(key, WildcardToRegex(filePattern), RegexOptions.IgnoreCase)) + { + var directoryName = Path.GetDirectoryName(key); + if (!dirs.Contains(directoryName)) + { + dirs.Add(directoryName); + } + } + } + return dirs.ToArray(); + } + + public string[] GetFiles(string sourceDirName) + { + HashSet files = new HashSet(); + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(sourceDirName) && VirtualStore[key] != FolderKey) + { + if (!files.Contains(key)) + { + files.Add(key); + } + } + } + return files.ToArray(); + } + + public string[] GetFiles(string startDirectory, string filePattern, SearchOption options) + { + HashSet files = new HashSet(); + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(startDirectory) && VirtualStore[key] != FolderKey && Regex.IsMatch(key, WildcardToRegex(filePattern), RegexOptions.IgnoreCase)) + { + if (!files.Contains(key)) + { + files.Add(key); + } + } + } + return files.ToArray(); + } + + public FileAttributes GetFileAttributes(string path) + { + if (VirtualStore[path] == FolderKey) + { + return FileAttributes.Directory; + } + if (VirtualStore.ContainsKey(path)) + { + return FileAttributes.Normal; + } + else + { + foreach (var key in VirtualStore.Keys.ToArray()) + { + if (key.StartsWith(path)) + { + return FileAttributes.Directory; + } + } + throw new IOException("File not found: " + path); + } + } + + public X509Certificate2 GetCertificate(string thumbprint) + { + if (thumbprint != null && certStore.ContainsKey(thumbprint)) + { + return certStore[thumbprint]; + } + else + { + return new X509Certificate2(); + } + } + + public void AddCertificate(X509Certificate2 cert) + { + if (cert != null && cert.Thumbprint != null) + { + certStore[cert.Thumbprint] = cert; + } + } + + public void RemoveCertificate(string thumbprint) + { + if (thumbprint != null && certStore.ContainsKey(thumbprint)) + { + certStore.Remove(thumbprint); + } + } + + /// + /// Converts unix asterisk based file pattern to regex + /// + /// Asterisk based pattern + /// Regeular expression of null is empty + private static string WildcardToRegex(string wildcard) + { + if (wildcard == null || wildcard == "") return wildcard; + + StringBuilder sb = new StringBuilder(); + + char[] chars = wildcard.ToCharArray(); + for (int i = 0; i < chars.Length; ++i) + { + if (chars[i] == '*') + sb.Append(".*"); + else if (chars[i] == '?') + sb.Append("."); + else if ("+()^$.{}|\\".IndexOf(chars[i]) != -1) + sb.Append('\\').Append(chars[i]); // prefix all metacharacters with backslash + else + sb.Append(chars[i]); + } + return sb.ToString().ToLowerInvariant(); + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/Properties/AssemblyInfo.cs b/src/Common/Commands.Common.Authentication.Test/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..0e37b664454e --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,33 @@ +// ---------------------------------------------------------------------------------- +// +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +using System; +using System.Reflection; +using System.Runtime.InteropServices; +using Microsoft.WindowsAzure.Commands.Common; +using Xunit; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Microsoft Azure Powershell - Common Authentication Profile Test")] +[assembly: AssemblyCompany(AzurePowerShell.AssemblyCompany)] +[assembly: AssemblyProduct(AzurePowerShell.AssemblyProduct)] +[assembly: AssemblyCopyright(AzurePowerShell.AssemblyCopyright)] + +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: AssemblyVersion("1.0.4")] +[assembly: AssemblyFileVersion("1.0.4")] +[assembly: CollectionBehavior(DisableTestParallelization = true)] diff --git a/src/Common/Commands.Common.Authentication.Test/Properties/Resources.Designer.cs b/src/Common/Commands.Common.Authentication.Test/Properties/Resources.Designer.cs new file mode 100644 index 000000000000..cdd60cdbad4a --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Properties/Resources.Designer.cs @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.34014 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Common.Authentication.Test.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Common.Authentication.Test.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] InvalidProfile { + get { + object obj = ResourceManager.GetObject("InvalidProfile", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ValidProfile { + get { + object obj = ResourceManager.GetObject("ValidProfile", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ValidProfile2 { + get { + object obj = ResourceManager.GetObject("ValidProfile2", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ValidProfile3 { + get { + object obj = ResourceManager.GetObject("ValidProfile3", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ValidProfileChina { + get { + object obj = ResourceManager.GetObject("ValidProfileChina", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] ValidProfileChinaOld { + get { + object obj = ResourceManager.GetObject("ValidProfileChinaOld", resourceCulture); + return ((byte[])(obj)); + } + } + } +} diff --git a/src/Common/Commands.Common.Authentication.Test/Properties/Resources.resx b/src/Common/Commands.Common.Authentication.Test/Properties/Resources.resx new file mode 100644 index 000000000000..4e9f161416ad --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Properties/Resources.resx @@ -0,0 +1,139 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\resources\invalidprofile.publishsettings;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\validprofile.publishsettings;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\validprofile2.publishsettings;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\validprofile3.publishsettings;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\validprofilechina.publishsettings;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\resources\validprofilechinaold.publishsettings;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/Azure.publishsettings b/src/Common/Commands.Common.Authentication.Test/Resources/Azure.publishsettings new file mode 100644 index 000000000000..aeae7405aa98 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/Azure.publishsettings @@ -0,0 +1,13 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/GB18030ServiceDefinition.csdef b/src/Common/Commands.Common.Authentication.Test/Resources/GB18030ServiceDefinition.csdef new file mode 100644 index 000000000000..db0be54262c4 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/GB18030ServiceDefinition.csdef @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/InvalidProfile.PublishSettings b/src/Common/Commands.Common.Authentication.Test/Resources/InvalidProfile.PublishSettings new file mode 100644 index 000000000000..3886328a747d --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/InvalidProfile.PublishSettings @@ -0,0 +1,16 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/ResourceLocator.cs b/src/Common/Commands.Common.Authentication.Test/Resources/ResourceLocator.cs new file mode 100644 index 000000000000..efccf6a8b006 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/ResourceLocator.cs @@ -0,0 +1,22 @@ +// ---------------------------------------------------------------------------------- +// Copyright Microsoft Corporation +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ---------------------------------------------------------------------------------- + +namespace Microsoft.WindowsAzure.Commands.Common.Test.Resources +{ + /// + /// A dummy class used to located the resources in this folder/namespace. + /// + public class ResourceLocator + { + } +} \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile.PublishSettings b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile.PublishSettings new file mode 100644 index 000000000000..f405a3be38b2 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile.PublishSettings @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile2.PublishSettings b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile2.PublishSettings new file mode 100644 index 000000000000..d2c13f8859e2 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile2.PublishSettings @@ -0,0 +1,15 @@ + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile3.PublishSettings b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile3.PublishSettings new file mode 100644 index 000000000000..455ad34a8022 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfile3.PublishSettings @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfileChina.PublishSettings b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfileChina.PublishSettings new file mode 100644 index 000000000000..4a533f0de98c --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfileChina.PublishSettings @@ -0,0 +1,14 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfileChinaOld.PublishSettings b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfileChinaOld.PublishSettings new file mode 100644 index 000000000000..41b00dffeb4b --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/ValidProfileChinaOld.PublishSettings @@ -0,0 +1,11 @@ + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/invalidsubscriptions.xml b/src/Common/Commands.Common.Authentication.Test/Resources/invalidsubscriptions.xml new file mode 100644 index 000000000000..426c453cd421 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/invalidsubscriptions.xml @@ -0,0 +1,4 @@ + + + This is a fake xml. + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/subscriptions.xml b/src/Common/Commands.Common.Authentication.Test/Resources/subscriptions.xml new file mode 100644 index 000000000000..a1c4c9b08732 --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/subscriptions.xml @@ -0,0 +1,15 @@ + + + + 279b0675-cf67-467f-98f0-67ae31eb540f + 12D09EC0008EEE10C1B80AB70B3739E6BC509BB3 + 0853C43B56C81CE8FC44C8ACDC8C54783C6080E2 + 0853C43B56C81CE8FC44C8ACDC8C54783C6080E2 + + + 279b0675-cf67-467f-98f0-67ae31eb540f + 12D09EC0008EEE10C1B80AB70B3739E6BC509BB3 + 0853C43B56C81CE8FC44C8ACDC8C54783C6080E2 + 0853C43B56C81CE8FC44C8ACDC8C54783C6080E2 + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/Resources/testruntimemanifest.xml b/src/Common/Commands.Common.Authentication.Test/Resources/testruntimemanifest.xml new file mode 100644 index 000000000000..518bce45a46c --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/Resources/testruntimemanifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication.Test/packages.config b/src/Common/Commands.Common.Authentication.Test/packages.config new file mode 100644 index 000000000000..6933da5a807e --- /dev/null +++ b/src/Common/Commands.Common.Authentication.Test/packages.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Common/Commands.Common.Authentication/Commands.Common.Authentication.csproj b/src/Common/Commands.Common.Authentication/Commands.Common.Authentication.csproj index 6d0cd4c93d78..0e7ed8e59545 100644 --- a/src/Common/Commands.Common.Authentication/Commands.Common.Authentication.csproj +++ b/src/Common/Commands.Common.Authentication/Commands.Common.Authentication.csproj @@ -51,7 +51,7 @@ - ..\..\packages\Hyak.Common.1.0.2\lib\net45\Hyak.Common.dll + ..\..\packages\Hyak.Common.1.0.3\lib\net45\Hyak.Common.dll True @@ -102,12 +102,12 @@ - - ..\..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Extensions.dll + + ..\..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Extensions.dll True - - ..\..\packages\Microsoft.Net.Http.2.2.22\lib\net45\System.Net.Http.Primitives.dll + + ..\..\packages\Microsoft.Net.Http.2.2.28\lib\net45\System.Net.Http.Primitives.dll True diff --git a/src/Common/Commands.Common.Authentication/packages.config b/src/Common/Commands.Common.Authentication/packages.config index 7cd3ca0ec601..a789212c6b80 100644 --- a/src/Common/Commands.Common.Authentication/packages.config +++ b/src/Common/Commands.Common.Authentication/packages.config @@ -1,13 +1,13 @@  - + - + diff --git a/src/Common/Commands.Common/Commands.Common.csproj b/src/Common/Commands.Common/Commands.Common.csproj index 4ce96d6c4039..93ff04d6eee5 100644 --- a/src/Common/Commands.Common/Commands.Common.csproj +++ b/src/Common/Commands.Common/Commands.Common.csproj @@ -66,10 +66,6 @@ False ..\..\packages\Microsoft.Azure.Common.2.1.0\lib\net45\Microsoft.Azure.Common.NetFramework.dll - - False - ..\..\packages\Microsoft.Azure.Management.Resources.2.18.11-preview\lib\net40\Microsoft.Azure.ResourceManager.dll - False ..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.18.206251556\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll diff --git a/src/Common/Commands.Common/packages.config b/src/Common/Commands.Common/packages.config index 223468584f83..64093cf24cf1 100644 --- a/src/Common/Commands.Common/packages.config +++ b/src/Common/Commands.Common/packages.config @@ -5,7 +5,6 @@ - diff --git a/src/Common/Commands.ScenarioTests.Common/Mocks/MockAccessTokenProvider.cs b/src/Common/Commands.ScenarioTests.Common/Mocks/MockAccessTokenProvider.cs index 7746be8a7416..737efe5cd1ea 100644 --- a/src/Common/Commands.ScenarioTests.Common/Mocks/MockAccessTokenProvider.cs +++ b/src/Common/Commands.ScenarioTests.Common/Mocks/MockAccessTokenProvider.cs @@ -13,14 +13,16 @@ // ---------------------------------------------------------------------------------- using System.Security; +using Microsoft.Azure.Commands.Common.Authentication; using Microsoft.Azure.Commands.Common.Authentication.Models; using Microsoft.WindowsAzure.Commands.Common.Test.Mocks; -using Microsoft.Azure.Commands.Common.Authentication; namespace Microsoft.WindowsAzure.Commands.Test.Utilities.Common { public class MockAccessTokenProvider : ITokenProvider { + public AdalConfiguration AdalConfiguration { get; set; } + private readonly IAccessToken accessToken; public MockAccessTokenProvider(string token) @@ -39,11 +41,13 @@ public MockAccessTokenProvider(string token, string userId) public IAccessToken GetAccessToken(AdalConfiguration config, ShowDialog promptBehavior, string userId, SecureString password, AzureAccount.AccountType credentialType) { + AdalConfiguration = config; return this.accessToken; } - public IAccessToken GetAccessTokenWithCertificate(AdalConfiguration config, string principalId, string certificateThumbprint, AzureAccount.AccountType credentialType) + public IAccessToken GetAccessTokenWithCertificate(AdalConfiguration config, string clientId, string certificateThumbprint, AzureAccount.AccountType credentialType) { + AdalConfiguration = config; return this.accessToken; } } diff --git a/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj b/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj index c0e1d49926b4..04e46664ca57 100644 --- a/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj +++ b/src/ResourceManager/Common/Commands.ResourceManager.Common/Commands.ResourceManager.Common.csproj @@ -64,6 +64,10 @@ False ..\..\..\packages\Microsoft.Azure.Common.2.1.0\lib\net45\Microsoft.Azure.Common.NetFramework.dll + + ..\..\..\packages\Microsoft.Azure.Management.Resources.2.18.14-preview\lib\net40\Microsoft.Azure.ResourceManager.dll + True + False ..\..\..\packages\Microsoft.IdentityModel.Clients.ActiveDirectory.2.18.206251556\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll diff --git a/src/ResourceManager/Common/Commands.ResourceManager.Common/packages.config b/src/ResourceManager/Common/Commands.ResourceManager.Common/packages.config index 64093cf24cf1..ea861073dadd 100644 --- a/src/ResourceManager/Common/Commands.ResourceManager.Common/packages.config +++ b/src/ResourceManager/Common/Commands.ResourceManager.Common/packages.config @@ -5,6 +5,7 @@ + diff --git a/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/Commands.ScenarioTests.ResourceManager.Common.csproj b/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/Commands.ScenarioTests.ResourceManager.Common.csproj index dbd224c0999b..bd0ca39315b6 100644 --- a/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/Commands.ScenarioTests.ResourceManager.Common.csproj +++ b/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/Commands.ScenarioTests.ResourceManager.Common.csproj @@ -49,8 +49,8 @@ ..\..\..\packages\Microsoft.Azure.Common.2.1.0\lib\net45\Microsoft.Azure.Common.NetFramework.dll - - ..\..\..\packages\Microsoft.Azure.Management.Resources.2.18.11-preview\lib\net40\Microsoft.Azure.ResourceManager.dll + + ..\..\..\packages\Microsoft.Azure.Management.Resources.2.18.14-preview\lib\net40\Microsoft.Azure.ResourceManager.dll True diff --git a/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/packages.config b/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/packages.config index 7c7ea25a2c34..4a974a900192 100644 --- a/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/packages.config +++ b/src/ResourceManager/Common/Commands.ScenarioTests.ResourceManager.Common/packages.config @@ -3,7 +3,7 @@ - + diff --git a/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj b/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj index 0386e28e89c8..cc0056bc0f9c 100644 --- a/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj +++ b/src/ResourceManager/Profile/Commands.Profile.Test/Commands.Profile.Test.csproj @@ -101,7 +101,7 @@ True - ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.Authentication.2.0.0-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.Authentication.dll + ..\..\..\packages\Microsoft.Rest.ClientRuntime.Azure.Authentication.2.0.1-preview\lib\net45\Microsoft.Rest.ClientRuntime.Azure.Authentication.dll True @@ -131,9 +131,9 @@ ..\..\..\packages\WindowsAzure.Storage.5.0.0\lib\net40\Microsoft.WindowsAzure.Storage.dll - - False - ..\..\..\packages\Moq.4.2.1402.2112\lib\net40\Moq.dll + + ..\..\..\packages\Moq.4.2.1409.1722\lib\net40\Moq.dll + True ..\..\..\packages\Newtonsoft.Json.6.0.8\lib\net45\Newtonsoft.Json.dll diff --git a/src/ResourceManager/Profile/Commands.Profile.Test/SessionRecords/Microsoft.Azure.Commands.Profile.Test.ProfileModuleTests/WarningOnIncompatibleVersions.json b/src/ResourceManager/Profile/Commands.Profile.Test/SessionRecords/Microsoft.Azure.Commands.Profile.Test.ProfileModuleTests/WarningOnIncompatibleVersions.json index f085c51a6519..b5ff7a318e86 100644 --- a/src/ResourceManager/Profile/Commands.Profile.Test/SessionRecords/Microsoft.Azure.Commands.Profile.Test.ProfileModuleTests/WarningOnIncompatibleVersions.json +++ b/src/ResourceManager/Profile/Commands.Profile.Test/SessionRecords/Microsoft.Azure.Commands.Profile.Test.ProfileModuleTests/WarningOnIncompatibleVersions.json @@ -1,5 +1,7 @@ { "Entries": [], "Names": {}, - "Variables": {} + "Variables": { + "SubscriptionId": "3ca49042-782a-4cc9-89b5-ee1b487fe115" + } } \ No newline at end of file diff --git a/src/ResourceManager/Profile/Commands.Profile.Test/packages.config b/src/ResourceManager/Profile/Commands.Profile.Test/packages.config index 13c36df51907..160cdf0b169a 100644 --- a/src/ResourceManager/Profile/Commands.Profile.Test/packages.config +++ b/src/ResourceManager/Profile/Commands.Profile.Test/packages.config @@ -16,12 +16,12 @@ - + - + diff --git a/src/ResourceManager/Profile/Profile.sln b/src/ResourceManager/Profile/Profile.sln index 092610418b22..f72eea717f79 100644 --- a/src/ResourceManager/Profile/Profile.sln +++ b/src/ResourceManager/Profile/Profile.sln @@ -1,6 +1,6 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.31101.0 +VisualStudioVersion = 12.0.40629.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{95C16AED-FD57-42A0-86C3-2CF4300A4817}" EndProject @@ -16,6 +16,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common", "..\..\Co EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Authentication", "..\..\Common\Commands.Common.Authentication\Commands.Common.Authentication.csproj", "{D3804B64-C0D3-48F8-82EC-1F632F833C9E}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.ScenarioTests.Common", "..\..\Common\Commands.ScenarioTests.Common\Commands.ScenarioTests.Common.csproj", "{C1BDA476-A5CC-4394-914D-48B0EC31A710}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.ServiceManagement.Common", "..\..\ServiceManagement\Common\Commands.ServiceManagement.Common\Commands.ServiceManagement.Common.csproj", "{CFF09E81-1E31-444E-B4D4-A21E946C29E2}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Commands.Common.Authentication.Test", "..\..\Common\Commands.Common.Authentication.Test\Commands.Common.Authentication.Test.csproj", "{C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -46,6 +52,18 @@ Global {D3804B64-C0D3-48F8-82EC-1F632F833C9E}.Debug|Any CPU.Build.0 = Debug|Any CPU {D3804B64-C0D3-48F8-82EC-1F632F833C9E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D3804B64-C0D3-48F8-82EC-1F632F833C9E}.Release|Any CPU.Build.0 = Release|Any CPU + {C1BDA476-A5CC-4394-914D-48B0EC31A710}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C1BDA476-A5CC-4394-914D-48B0EC31A710}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C1BDA476-A5CC-4394-914D-48B0EC31A710}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C1BDA476-A5CC-4394-914D-48B0EC31A710}.Release|Any CPU.Build.0 = Release|Any CPU + {CFF09E81-1E31-444E-B4D4-A21E946C29E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CFF09E81-1E31-444E-B4D4-A21E946C29E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CFF09E81-1E31-444E-B4D4-A21E946C29E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CFF09E81-1E31-444E-B4D4-A21E946C29E2}.Release|Any CPU.Build.0 = Release|Any CPU + {C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -53,5 +71,8 @@ Global GlobalSection(NestedProjects) = preSolution {152D78F0-A642-4D0E-B3A8-2FC64FFA9714} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} {3436A126-EDC9-4060-8952-9A1BE34CDD95} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} + {C1BDA476-A5CC-4394-914D-48B0EC31A710} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} + {CFF09E81-1E31-444E-B4D4-A21E946C29E2} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} + {C2CF99A2-D35E-4AED-AFB9-C26960AF1D0D} = {95C16AED-FD57-42A0-86C3-2CF4300A4817} EndGlobalSection EndGlobal