From 1a7e01ac1879dff80f448955d3b23ff18a84c604 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 2 Mar 2021 13:18:52 -0800 Subject: [PATCH 01/10] Divide project into 2 assemblies. --- .../Authentication.Core/Authenticator.cs | 129 ++++++++++++++++++ .../Common/GraphSession.cs | 3 +- .../Authentication.Core/Constants.cs | 18 +++ .../Authentication.Core/ErrorConstants.cs | 31 +++++ .../Interfaces/IAuthContext.cs | 0 .../Interfaces/IDataStore.cs | 0 .../Interfaces/IGraphEnvironment.cs | 0 .../Interfaces/IGraphSession.cs | 0 ...Microsoft.Graph.Authentication.Core.csproj | 30 ++++ .../Models/JwtPayload.cs | 0 .../TokenCache/LinuxTokenCache.cs | 1 + .../TokenCache/MacTokenCache.cs | 1 + .../NativePlatformLibs/LinuxNativeKeyUtils.cs | 0 .../NativePlatformLibs/MacNativeKeyChain.cs | 0 .../TokenCache/TokenCacheStorage.cs | 1 + .../TokenCache/WindowsTokenCache.cs | 1 + .../Utilities}/AuthenticationHelpers.cs | 10 +- .../Utilities}/JwtHelpers.cs | 6 +- .../Utilities}/PlatformHelpers.cs | 2 +- src/Authentication/Authentication.sln | 8 +- .../Authentication/Cmdlets/ConnectMgGraph.cs | 105 ++------------ .../Cmdlets/DisconnectMgGraph.cs | 9 +- .../Authentication/Constants.cs | 8 +- .../Authentication/ErrorConstants.cs | 20 +-- .../Authentication/Helpers/HttpHelpers.cs | 2 +- .../Microsoft.Graph.Authentication.csproj | 25 +--- .../Microsoft.Graph.Authentication.nuspec | 28 ++-- .../Microsoft.Graph.Authentication.psd1 | 2 +- .../Microsoft.Graph.Authentication.psm1 | 12 +- .../Utilities/DependencyAssemblyResolver.cs | 68 +++++++++ .../Authentication/build-module.ps1 | 84 +++++++++--- 31 files changed, 401 insertions(+), 203 deletions(-) create mode 100644 src/Authentication/Authentication.Core/Authenticator.cs rename src/Authentication/{Authentication => Authentication.Core}/Common/GraphSession.cs (98%) create mode 100644 src/Authentication/Authentication.Core/Constants.cs create mode 100644 src/Authentication/Authentication.Core/ErrorConstants.cs rename src/Authentication/{Authentication => Authentication.Core}/Interfaces/IAuthContext.cs (100%) rename src/Authentication/{Authentication => Authentication.Core}/Interfaces/IDataStore.cs (100%) rename src/Authentication/{Authentication => Authentication.Core}/Interfaces/IGraphEnvironment.cs (100%) rename src/Authentication/{Authentication => Authentication.Core}/Interfaces/IGraphSession.cs (100%) create mode 100644 src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj rename src/Authentication/{Authentication => Authentication.Core}/Models/JwtPayload.cs (100%) rename src/Authentication/{Authentication => Authentication.Core}/TokenCache/LinuxTokenCache.cs (98%) rename src/Authentication/{Authentication => Authentication.Core}/TokenCache/MacTokenCache.cs (99%) rename src/Authentication/{Authentication => Authentication.Core}/TokenCache/NativePlatformLibs/LinuxNativeKeyUtils.cs (100%) rename src/Authentication/{Authentication => Authentication.Core}/TokenCache/NativePlatformLibs/MacNativeKeyChain.cs (100%) rename src/Authentication/{Authentication => Authentication.Core}/TokenCache/TokenCacheStorage.cs (98%) rename src/Authentication/{Authentication => Authentication.Core}/TokenCache/WindowsTokenCache.cs (98%) rename src/Authentication/{Authentication/Helpers => Authentication.Core/Utilities}/AuthenticationHelpers.cs (95%) rename src/Authentication/{Authentication/Helpers => Authentication.Core/Utilities}/JwtHelpers.cs (94%) rename src/Authentication/{Authentication/Helpers => Authentication.Core/Utilities}/PlatformHelpers.cs (96%) create mode 100644 src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs diff --git a/src/Authentication/Authentication.Core/Authenticator.cs b/src/Authentication/Authentication.Core/Authenticator.cs new file mode 100644 index 00000000000..bb475d5a5cb --- /dev/null +++ b/src/Authentication/Authentication.Core/Authenticator.cs @@ -0,0 +1,129 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ + +namespace Microsoft.Graph.Authentication.Core +{ + using Microsoft.Graph.Auth; + using Microsoft.Graph.PowerShell.Authentication; + using Microsoft.Graph.PowerShell.Authentication.Core; + using Microsoft.Graph.PowerShell.Authentication.Helpers; + using Microsoft.Graph.PowerShell.Authentication.Models; + using Microsoft.Identity.Client; + using System; + using System.Collections.Generic; + using System.Globalization; + using System.Linq; + using System.Net.Http; + using System.Threading; + using System.Threading.Tasks; + + public static class Authenticator + { + public static async Task AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken) + { + try + { + // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. + IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); + IClientApplicationBase clientApplication = null; + if (authContext.AuthType == AuthenticationType.Delegated) + { + clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; + } + if (authContext.AuthType == AuthenticationType.AppOnly) + { + clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; + } + + // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. + GraphRequestContext graphRequestContext = new GraphRequestContext(); + graphRequestContext.CancellationToken = cancellationToken; + graphRequestContext.MiddlewareOptions = new Dictionary + { + { + typeof(AuthenticationHandlerOption).ToString(), + new AuthenticationHandlerOption + { + AuthenticationProviderOption = new AuthenticationProviderOption + { + Scopes = authContext.Scopes, + ForceRefresh = forceRefresh + } + } + } + }; + + // Trigger consent. + HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); + httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); + await authProvider.AuthenticateRequestAsync(httpRequestMessage); + + IAccount account = null; + if (clientApplication != null) + { + // Only get accounts when we are using MSAL to get an access token. + IEnumerable accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); + account = accounts.FirstOrDefault(); + } + + DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); + return authContext; + } + catch (AuthenticationException authEx) + { + if ((authEx.InnerException is TaskCanceledException) && cancellationToken.IsCancellationRequested) + { + // DeviceCodeTimeout + throw new Exception(string.Format( + CultureInfo.CurrentCulture, + ErrorConstants.Message.DeviceCodeTimeout, + Constants.MaxDeviceCodeTimeOut)); + } + else + { + throw authEx.InnerException ?? authEx; + } + } + catch (Exception ex) + { + throw ex.InnerException ?? ex; + } + } + + public static void LogOut(IAuthContext authContext) + { + AuthenticationHelpers.Logout(authContext); + } + + private static void DecodeJWT(string token, IAccount account, ref IAuthContext authContext) + { + JwtPayload jwtPayload = JwtHelpers.DecodeToObject(token); + if (authContext.AuthType == AuthenticationType.UserProvidedAccessToken) + { + if (jwtPayload == null) + { + throw new Exception(string.Format( + CultureInfo.CurrentCulture, + ErrorConstants.Message.InvalidUserProvidedToken, + "AccessToken")); + } + + if (jwtPayload.Exp <= JwtHelpers.ConvertToUnixTimestamp(DateTime.UtcNow + TimeSpan.FromMinutes(Constants.TokenExpirationBufferInMinutes))) + { + throw new Exception(string.Format( + CultureInfo.CurrentCulture, + ErrorConstants.Message.ExpiredUserProvidedToken, + "AccessToken")); + } + } + + authContext.ClientId = jwtPayload?.Appid ?? authContext.ClientId; + authContext.Scopes = jwtPayload?.Scp?.Split(' ') ?? jwtPayload?.Roles; + authContext.TenantId = jwtPayload?.Tid ?? account?.HomeAccountId?.TenantId; + authContext.AppName = jwtPayload?.AppDisplayname; + authContext.Account = jwtPayload?.Upn ?? account?.Username; + } + + } +} diff --git a/src/Authentication/Authentication/Common/GraphSession.cs b/src/Authentication/Authentication.Core/Common/GraphSession.cs similarity index 98% rename from src/Authentication/Authentication/Common/GraphSession.cs rename to src/Authentication/Authentication.Core/Common/GraphSession.cs index 275b4a190b0..9de7e41c12d 100644 --- a/src/Authentication/Authentication/Common/GraphSession.cs +++ b/src/Authentication/Authentication.Core/Common/GraphSession.cs @@ -4,6 +4,7 @@ namespace Microsoft.Graph.PowerShell.Authentication { + using Microsoft.Graph.PowerShell.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.Interfaces; using System; using System.Security; @@ -175,7 +176,7 @@ public static void Modify(Action modifier) /// /// Resets the current instance of to initial state. /// - internal static void Reset() + public static void Reset() { try { diff --git a/src/Authentication/Authentication.Core/Constants.cs b/src/Authentication/Authentication.Core/Constants.cs new file mode 100644 index 00000000000..2b7675a6089 --- /dev/null +++ b/src/Authentication/Authentication.Core/Constants.cs @@ -0,0 +1,18 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ + +namespace Microsoft.Graph.PowerShell.Authentication.Core +{ + using System.IO; + public static class Constants + { + public const int MaxDeviceCodeTimeOut = 120; // 2 mins timeout. + public static readonly string GraphDirectoryPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), ".graph"); + internal const string TokenCacheServiceName = "com.microsoft.graph.powershell.sdkcache"; + internal const string DefaultProfile = "v1.0"; + internal const int TokenExpirationBufferInMinutes = 5; + internal const string DefaulAdTenant = "common"; + internal const string DefaultAzureADEndpoint = "https://login.microsoftonline.com"; + } +} diff --git a/src/Authentication/Authentication.Core/ErrorConstants.cs b/src/Authentication/Authentication.Core/ErrorConstants.cs new file mode 100644 index 00000000000..acd1add6348 --- /dev/null +++ b/src/Authentication/Authentication.Core/ErrorConstants.cs @@ -0,0 +1,31 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ +using System; + +namespace Microsoft.Graph.PowerShell.Authentication.Core +{ + public static class ErrorConstants + { + internal static class Codes + { + internal const string SessionNotInitialized = "sessionNotInitialized"; + internal const string SessionLockReadRecursion = "sessionLockReadRecursion"; + internal const string SessionLockReadDisposed = "sessionLockReadDisposed"; + internal const string SessionLockWriteDisposed = "sessionLockWriteDisposed"; + internal const string SessionLockWriteRecursion = "sessionLockWriteRecursion"; + internal const string InvalidJWT = "invalidJWT"; + } + + public static class Message + { + internal const string InvalidJWT = "Invalid JWT access token."; + public const string MissingAuthContext = "Authentication needed, call Connect-MgGraph."; + internal const string NullOrEmptyParameter = "Parameter '{0}' cannot be null or empty."; + internal const string MacKeyChainFailed = "{0} failed with result code {1}."; + internal const string DeviceCodeTimeout = "Device code terminal timed-out after {0} seconds. Please try again."; + internal const string InvalidUserProvidedToken = "The provided access token is invalid. Set a valid access token to `-{0}` parameter and try again."; + internal const string ExpiredUserProvidedToken = "The provided access token has expired. Set a valid access token to `-{0}` parameter and try again."; + } + } +} diff --git a/src/Authentication/Authentication/Interfaces/IAuthContext.cs b/src/Authentication/Authentication.Core/Interfaces/IAuthContext.cs similarity index 100% rename from src/Authentication/Authentication/Interfaces/IAuthContext.cs rename to src/Authentication/Authentication.Core/Interfaces/IAuthContext.cs diff --git a/src/Authentication/Authentication/Interfaces/IDataStore.cs b/src/Authentication/Authentication.Core/Interfaces/IDataStore.cs similarity index 100% rename from src/Authentication/Authentication/Interfaces/IDataStore.cs rename to src/Authentication/Authentication.Core/Interfaces/IDataStore.cs diff --git a/src/Authentication/Authentication/Interfaces/IGraphEnvironment.cs b/src/Authentication/Authentication.Core/Interfaces/IGraphEnvironment.cs similarity index 100% rename from src/Authentication/Authentication/Interfaces/IGraphEnvironment.cs rename to src/Authentication/Authentication.Core/Interfaces/IGraphEnvironment.cs diff --git a/src/Authentication/Authentication/Interfaces/IGraphSession.cs b/src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs similarity index 100% rename from src/Authentication/Authentication/Interfaces/IGraphSession.cs rename to src/Authentication/Authentication.Core/Interfaces/IGraphSession.cs diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj new file mode 100644 index 00000000000..0b3d23b50e0 --- /dev/null +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -0,0 +1,30 @@ + + + + + netstandard2.0 + Microsoft.Graph.PowerShell.Authentication.Core + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Authentication/Authentication/Models/JwtPayload.cs b/src/Authentication/Authentication.Core/Models/JwtPayload.cs similarity index 100% rename from src/Authentication/Authentication/Models/JwtPayload.cs rename to src/Authentication/Authentication.Core/Models/JwtPayload.cs diff --git a/src/Authentication/Authentication/TokenCache/LinuxTokenCache.cs b/src/Authentication/Authentication.Core/TokenCache/LinuxTokenCache.cs similarity index 98% rename from src/Authentication/Authentication/TokenCache/LinuxTokenCache.cs rename to src/Authentication/Authentication.Core/TokenCache/LinuxTokenCache.cs index f195708c02b..1c73c082c29 100644 --- a/src/Authentication/Authentication/TokenCache/LinuxTokenCache.cs +++ b/src/Authentication/Authentication.Core/TokenCache/LinuxTokenCache.cs @@ -4,6 +4,7 @@ namespace Microsoft.Graph.PowerShell.Authentication.TokenCache { + using Microsoft.Graph.PowerShell.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.TokenCache.NativePlatformLibs; using System; using System.Globalization; diff --git a/src/Authentication/Authentication/TokenCache/MacTokenCache.cs b/src/Authentication/Authentication.Core/TokenCache/MacTokenCache.cs similarity index 99% rename from src/Authentication/Authentication/TokenCache/MacTokenCache.cs rename to src/Authentication/Authentication.Core/TokenCache/MacTokenCache.cs index 3ac7a642300..b06a641dac4 100644 --- a/src/Authentication/Authentication/TokenCache/MacTokenCache.cs +++ b/src/Authentication/Authentication.Core/TokenCache/MacTokenCache.cs @@ -4,6 +4,7 @@ namespace Microsoft.Graph.PowerShell.Authentication.TokenCache { + using Microsoft.Graph.PowerShell.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.TokenCache.NativePlatformLibs; using System; using System.Globalization; diff --git a/src/Authentication/Authentication/TokenCache/NativePlatformLibs/LinuxNativeKeyUtils.cs b/src/Authentication/Authentication.Core/TokenCache/NativePlatformLibs/LinuxNativeKeyUtils.cs similarity index 100% rename from src/Authentication/Authentication/TokenCache/NativePlatformLibs/LinuxNativeKeyUtils.cs rename to src/Authentication/Authentication.Core/TokenCache/NativePlatformLibs/LinuxNativeKeyUtils.cs diff --git a/src/Authentication/Authentication/TokenCache/NativePlatformLibs/MacNativeKeyChain.cs b/src/Authentication/Authentication.Core/TokenCache/NativePlatformLibs/MacNativeKeyChain.cs similarity index 100% rename from src/Authentication/Authentication/TokenCache/NativePlatformLibs/MacNativeKeyChain.cs rename to src/Authentication/Authentication.Core/TokenCache/NativePlatformLibs/MacNativeKeyChain.cs diff --git a/src/Authentication/Authentication/TokenCache/TokenCacheStorage.cs b/src/Authentication/Authentication.Core/TokenCache/TokenCacheStorage.cs similarity index 98% rename from src/Authentication/Authentication/TokenCache/TokenCacheStorage.cs rename to src/Authentication/Authentication.Core/TokenCache/TokenCacheStorage.cs index b1796dc5e73..dce951e1800 100644 --- a/src/Authentication/Authentication/TokenCache/TokenCacheStorage.cs +++ b/src/Authentication/Authentication.Core/TokenCache/TokenCacheStorage.cs @@ -1,6 +1,7 @@ // ------------------------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. // ------------------------------------------------------------------------------ +using Microsoft.Graph.PowerShell.Authentication.Core; using System; using System.Globalization; using System.Security; diff --git a/src/Authentication/Authentication/TokenCache/WindowsTokenCache.cs b/src/Authentication/Authentication.Core/TokenCache/WindowsTokenCache.cs similarity index 98% rename from src/Authentication/Authentication/TokenCache/WindowsTokenCache.cs rename to src/Authentication/Authentication.Core/TokenCache/WindowsTokenCache.cs index fa2612ab814..335c87bb632 100644 --- a/src/Authentication/Authentication/TokenCache/WindowsTokenCache.cs +++ b/src/Authentication/Authentication.Core/TokenCache/WindowsTokenCache.cs @@ -4,6 +4,7 @@ namespace Microsoft.Graph.PowerShell.Authentication.TokenCache { + using Microsoft.Graph.PowerShell.Authentication.Core; using System; using System.Globalization; using System.IO; diff --git a/src/Authentication/Authentication/Helpers/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs similarity index 95% rename from src/Authentication/Authentication/Helpers/AuthenticationHelpers.cs rename to src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 93631d0a257..1359c454b67 100644 --- a/src/Authentication/Authentication/Helpers/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -4,7 +4,7 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers { using Microsoft.Graph.Auth; - using Microsoft.Graph.PowerShell.Authentication.Models; + using Microsoft.Graph.PowerShell.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.TokenCache; using Microsoft.Identity.Client; @@ -18,11 +18,11 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers using AuthenticationException = System.Security.Authentication.AuthenticationException; - internal static class AuthenticationHelpers + public static class AuthenticationHelpers { static ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); - internal static IAuthenticationProvider GetAuthProvider(IAuthContext authContext) + public static IAuthenticationProvider GetAuthProvider(IAuthContext authContext) { if (authContext is null) { @@ -107,8 +107,8 @@ private static X509Certificate2 GetCertificate(IAuthContext context) private static string GetAuthorityUrl(IAuthContext authContext) { - string audience = authContext.TenantId ?? GraphEnvironmentConstants.CommonAdTenant; - string defaultInstance = GraphEnvironment.BuiltInEnvironments[GraphEnvironmentConstants.EnvironmentName.Global].AzureADEndpoint; + string audience = authContext.TenantId ?? Constants.DefaulAdTenant; + string defaultInstance = Constants.DefaultAzureADEndpoint; string authorityUrl = $"{defaultInstance}/{audience}"; if (GraphSession.Instance.Environment != null) diff --git a/src/Authentication/Authentication/Helpers/JwtHelpers.cs b/src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs similarity index 94% rename from src/Authentication/Authentication/Helpers/JwtHelpers.cs rename to src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs index a8e45b9165c..2a256f6bffc 100644 --- a/src/Authentication/Authentication/Helpers/JwtHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs @@ -5,14 +5,10 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers { using Microsoft.Graph.Auth; - using Microsoft.IdentityModel.Tokens; + using Microsoft.Graph.PowerShell.Authentication.Core; using Newtonsoft.Json; - using Newtonsoft.Json.Linq; using System; - using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; - using System.Security.Claims; - using System.Text; /// /// A JwtHelpers class. diff --git a/src/Authentication/Authentication/Helpers/PlatformHelpers.cs b/src/Authentication/Authentication.Core/Utilities/PlatformHelpers.cs similarity index 96% rename from src/Authentication/Authentication/Helpers/PlatformHelpers.cs rename to src/Authentication/Authentication.Core/Utilities/PlatformHelpers.cs index cbb9e60ba16..0c4c98234fc 100644 --- a/src/Authentication/Authentication/Helpers/PlatformHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/PlatformHelpers.cs @@ -6,7 +6,7 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers { using System.Runtime.InteropServices; - internal static class OperatingSystem + public static class OperatingSystem { /// /// Detects if the platform we are running on is Windows. diff --git a/src/Authentication/Authentication.sln b/src/Authentication/Authentication.sln index fd8252ae061..c24efddc101 100644 --- a/src/Authentication/Authentication.sln +++ b/src/Authentication/Authentication.sln @@ -5,7 +5,9 @@ VisualStudioVersion = 16.0.29201.188 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Graph.Authentication", "Authentication\Microsoft.Graph.Authentication.csproj", "{44FF315A-27B2-4401-81A9-1912E6511EE6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Graph.Authentication.Test", "Authentication.Test\Microsoft.Graph.Authentication.Test.csproj", "{416590B4-3A91-4B0D-9B40-3F69438B6D85}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Graph.Authentication.Test", "Authentication.Test\Microsoft.Graph.Authentication.Test.csproj", "{416590B4-3A91-4B0D-9B40-3F69438B6D85}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Graph.Authentication.Core", "Authentication.Core\Microsoft.Graph.Authentication.Core.csproj", "{50050576-74B8-4507-B1FE-C47740BB3B71}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -21,6 +23,10 @@ Global {416590B4-3A91-4B0D-9B40-3F69438B6D85}.Debug|Any CPU.Build.0 = Debug|Any CPU {416590B4-3A91-4B0D-9B40-3F69438B6D85}.Release|Any CPU.ActiveCfg = Release|Any CPU {416590B4-3A91-4B0D-9B40-3F69438B6D85}.Release|Any CPU.Build.0 = Release|Any CPU + {50050576-74B8-4507-B1FE-C47740BB3B71}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50050576-74B8-4507-B1FE-C47740BB3B71}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50050576-74B8-4507-B1FE-C47740BB3B71}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50050576-74B8-4507-B1FE-C47740BB3B71}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs b/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs index 4bd14d38df0..218613199f1 100644 --- a/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs +++ b/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs @@ -7,17 +7,12 @@ namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets using System.Collections.Generic; using System.Linq; using System.Management.Automation; - using System.Net.Http; using System.Threading; using System.Threading.Tasks; using System.Net; - using System.Globalization; using System.Collections; using System.Security.Cryptography.X509Certificates; - using Identity.Client; - - using Microsoft.Graph.Auth; using Microsoft.Graph.PowerShell.Authentication.Helpers; using Microsoft.Graph.PowerShell.Authentication.Models; @@ -25,6 +20,8 @@ namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets using Common; using static Helpers.AsyncHelpers; + using Microsoft.Graph.Authentication.Core; + using Microsoft.Graph.PowerShell.Authentication.Utilities; [Cmdlet(VerbsCommunications.Connect, "MgGraph", DefaultParameterSetName = Constants.UserParameterSet)] [Alias("Connect-Graph")] @@ -168,7 +165,7 @@ private async Task ProcessRecordAsync() case Constants.UserParameterSet: { // 2 mins timeout. 1 min < HTTP timeout. - TimeSpan authTimeout = new TimeSpan(0, 0, Constants.MaxDeviceCodeTimeOut); + TimeSpan authTimeout = new TimeSpan(0, 0, Core.Constants.MaxDeviceCodeTimeOut); // To avoid re-initializing the tokenSource, use CancelAfter _cancellationTokenSource.CancelAfter(authTimeout); authContext.AuthType = AuthenticationType.Delegated; @@ -201,71 +198,12 @@ private async Task ProcessRecordAsync() try { - // Gets a static instance of IAuthenticationProvider when the client app hasn't changed. - IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); - IClientApplicationBase clientApplication = null; - if (ParameterSetName == Constants.UserParameterSet) - { - clientApplication = (authProvider as DeviceCodeProvider).ClientApplication; - } - else if (ParameterSetName == Constants.AppParameterSet) - { - clientApplication = (authProvider as ClientCredentialProvider).ClientApplication; - } - - // Incremental scope consent without re-instantiating the auth provider. We will use a static instance. - GraphRequestContext graphRequestContext = new GraphRequestContext(); - graphRequestContext.CancellationToken = _cancellationTokenSource.Token; - graphRequestContext.MiddlewareOptions = new Dictionary - { - { - typeof(AuthenticationHandlerOption).ToString(), - new AuthenticationHandlerOption - { - AuthenticationProviderOption = new AuthenticationProviderOption - { - Scopes = authContext.Scopes, - ForceRefresh = ForceRefresh - } - } - } - }; - - // Trigger consent. - HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me"); - httpRequestMessage.Properties.Add(typeof(GraphRequestContext).ToString(), graphRequestContext); - await authProvider.AuthenticateRequestAsync(httpRequestMessage); - - IAccount account = null; - if (clientApplication != null) - { - // Only get accounts when we are using MSAL to get an access token. - IEnumerable accounts = clientApplication.GetAccountsAsync().GetAwaiter().GetResult(); - account = accounts.FirstOrDefault(); - } - DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); - // Save auth context to session state. - GraphSession.Instance.AuthContext = authContext; - } - catch (AuthenticationException authEx) - { - if ((authEx.InnerException is TaskCanceledException) && _cancellationTokenSource.Token.IsCancellationRequested) - { - // DeviceCodeTimeout - throw new Exception(string.Format( - CultureInfo.CurrentCulture, - ErrorConstants.Message.DeviceCodeTimeout, - Constants.MaxDeviceCodeTimeOut)); - } - else - { - throw authEx.InnerException ?? authEx; - } + GraphSession.Instance.AuthContext = await Authenticator.AuthenticateAsync(authContext, ForceRefresh, _cancellationTokenSource.Token); } - catch (Exception ex) + catch(Exception ex) { - throw ex.InnerException ?? ex; + throw ex; } WriteObject("Welcome To Microsoft Graph!"); @@ -348,40 +286,12 @@ private void ValidateParameters() } } - private void DecodeJWT(string token, IAccount account, ref IAuthContext authContext) - { - JwtPayload jwtPayload = JwtHelpers.DecodeToObject(token); - if (authContext.AuthType == AuthenticationType.UserProvidedAccessToken) - { - if (jwtPayload == null) - { - throw new Exception(string.Format( - CultureInfo.CurrentCulture, - ErrorConstants.Message.InvalidUserProvidedToken, - nameof(AccessToken))); - } - - if (jwtPayload.Exp <= JwtHelpers.ConvertToUnixTimestamp(DateTime.UtcNow + TimeSpan.FromMinutes(Constants.TokenExpirationBufferInMinutes))) - { - throw new Exception(string.Format( - CultureInfo.CurrentCulture, - ErrorConstants.Message.ExpiredUserProvidedToken, - nameof(AccessToken))); - } - } - - authContext.ClientId = jwtPayload?.Appid ?? authContext.ClientId; - authContext.Scopes = jwtPayload?.Scp?.Split(' ') ?? jwtPayload?.Roles; - authContext.TenantId = jwtPayload?.Tid ?? account?.HomeAccountId?.TenantId; - authContext.AppName = jwtPayload?.AppDisplayname; - authContext.Account = jwtPayload?.Upn ?? account?.Username; - } - /// /// Globally initializes GraphSession. /// public void OnImport() { + DependencyAssemblyResolver.Initialize(); GraphSessionInitializer.InitializeSession(); GraphSession.Instance.DataStore = new DiskDataStore(); } @@ -393,6 +303,7 @@ public void OnImport() public void OnRemove(PSModuleInfo psModuleInfo) { GraphSession.Reset(); + DependencyAssemblyResolver.Reset(); } } } diff --git a/src/Authentication/Authentication/Cmdlets/DisconnectMgGraph.cs b/src/Authentication/Authentication/Cmdlets/DisconnectMgGraph.cs index 8e38062da9b..a059cc595aa 100644 --- a/src/Authentication/Authentication/Cmdlets/DisconnectMgGraph.cs +++ b/src/Authentication/Authentication/Cmdlets/DisconnectMgGraph.cs @@ -3,6 +3,7 @@ // ------------------------------------------------------------------------------ namespace Microsoft.Graph.PowerShell.Authentication.Cmdlets { + using Microsoft.Graph.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.Helpers; using System; using System.Management.Automation; @@ -24,13 +25,13 @@ protected override void ProcessRecord() { base.ProcessRecord(); - IAuthContext authConfig = GraphSession.Instance.AuthContext; + IAuthContext authContext = GraphSession.Instance.AuthContext; - if (authConfig == null) + if (authContext == null) ThrowTerminatingError( - new ErrorRecord(new System.Exception("No application to sign out from."), Guid.NewGuid().ToString(), ErrorCategory.InvalidArgument, null)); + new ErrorRecord(new Exception("No application to sign out from."), Guid.NewGuid().ToString(), ErrorCategory.InvalidArgument, null)); - AuthenticationHelpers.Logout(authConfig); + Authenticator.LogOut(authContext); GraphSession.Instance.AuthContext = null; } diff --git a/src/Authentication/Authentication/Constants.cs b/src/Authentication/Authentication/Constants.cs index 8a4cac07642..62ba55bc18b 100644 --- a/src/Authentication/Authentication/Constants.cs +++ b/src/Authentication/Authentication/Constants.cs @@ -7,17 +7,11 @@ namespace Microsoft.Graph.PowerShell.Authentication using System.IO; public static class Constants { - public const string GraphAuthConfigId = "GraphAuthConfigId"; public const string SDKHeaderValue = "Graph-powershell-{0}-{1}.{2}.{3}"; internal const string UserParameterSet = "UserParameterSet"; internal const string AppParameterSet = "AppParameterSet"; internal const string AccessTokenParameterSet = "AccessTokenParameterSet"; - internal const int MaxDeviceCodeTimeOut = 120; // 2 mins timeout. - internal static readonly string GraphDirectoryPath = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), ".graph"); - internal static readonly string SettingFilePath = Path.Combine(GraphDirectoryPath, "GraphContext.json"); + internal static readonly string SettingFilePath = Path.Combine(Core.Constants.GraphDirectoryPath, "GraphContext.json"); internal const string ProfileDescription = "A snapshot of the Microsoft Graph {0} API for {1} cloud."; - internal const string TokenCacheServiceName = "com.microsoft.graph.powershell.sdkcache"; - internal const string DefaultProfile = "v1.0"; - internal const int TokenExpirationBufferInMinutes = 5; } } diff --git a/src/Authentication/Authentication/ErrorConstants.cs b/src/Authentication/Authentication/ErrorConstants.cs index f9f6cf2fa7d..533014ca92f 100644 --- a/src/Authentication/Authentication/ErrorConstants.cs +++ b/src/Authentication/Authentication/ErrorConstants.cs @@ -9,12 +9,6 @@ public static class ErrorConstants { internal static class Codes { - internal const string SessionNotInitialized = "sessionNotInitialized"; - internal const string SessionLockReadRecursion = "sessionLockReadRecursion"; - internal const string SessionLockReadDisposed = "sessionLockReadDisposed"; - internal const string SessionLockWriteDisposed = "sessionLockWriteDisposed"; - internal const string SessionLockWriteRecursion = "sessionLockWriteRecursion"; - internal const string InvalidJWT = "invalidJWT"; internal const string InvokeGraphHttpResponseException = nameof(InvokeGraphHttpResponseException); internal const string InvokeGraphContentTypeException = nameof(InvokeGraphContentTypeException); internal const string InvokeGraphRequestInvalidHost = nameof(InvokeGraphRequestInvalidHost); @@ -35,20 +29,12 @@ internal static class Codes public const string InvokeGraphRequestCouldNotInferFileName = nameof(InvokeGraphRequestKeysWithDifferentCasingInJsonString); } - internal static class Message + public static class Message { - internal const string InvalidJWT = "Invalid JWT access token."; - internal const string MissingAuthContext = "Authentication needed, call Connect-MgGraph."; - internal const string NullOrEmptyParameter = "Parameter '{0}' cannot be null or empty."; - internal const string MacKeyChainFailed = "{0} failed with result code {1}."; - internal const string DeviceCodeTimeout = "Device code terminal timed-out after {0} seconds. Please try again."; - internal const string InvalidUserProvidedToken = "The provided access token is invalid. Set a valid access token to `-{0}` parameter and try again."; - internal const string ExpiredUserProvidedToken = "The provided access token has expired. Set a valid access token to `-{0}` parameter and try again."; - internal const string InvalidUrlParameter = "Parameter '{0}' has an invalid endpoint URL. Please use a valid URL with a network protocol i.e. [protocol]://[resource-name]."; - internal const string InvalidNationalCloud = "Parameter `{0}` has an invalid national cloud. Use Get-MgEnvironment to get a list of valid national clouds."; + public const string CannotModifyBuiltInEnvironment = "Cannot {0} built-in environment {1}."; + public const string InvalidUrlParameter = "Parameter '{0}' has an invalid endpoint URL. Please use a valid URL with a network protocol i.e. [protocol]://[resource-name]."; internal const string InvalidEnvironment = "Unable to find environment with name '{0}'. Use Get-MgEnvironment to list available environments."; internal const string CannotAccessFile = "Could not {0} file at '{1}'. Please ensure you have access to this file and try again in a few minutes.."; - internal const string CannotModifyBuiltInEnvironment = "Cannot {0} built-in environment {1}."; internal const string InvalidCertificateThumbprint = "'{0}' must have a length of 40. Ensure you have the right certificate thumbprint then try again."; } } diff --git a/src/Authentication/Authentication/Helpers/HttpHelpers.cs b/src/Authentication/Authentication/Helpers/HttpHelpers.cs index 7732252c06f..1eab2b43dd6 100644 --- a/src/Authentication/Authentication/Helpers/HttpHelpers.cs +++ b/src/Authentication/Authentication/Helpers/HttpHelpers.cs @@ -37,7 +37,7 @@ public static HttpClient GetGraphHttpClient(IAuthContext authContext = null) authContext = authContext ?? GraphSession.Instance.AuthContext; if (authContext is null) { - throw new AuthenticationException(ErrorConstants.Message.MissingAuthContext); + throw new AuthenticationException(Core.ErrorConstants.Message.MissingAuthContext); } IAuthenticationProvider authProvider = AuthenticationHelpers.GetAuthProvider(authContext); diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj b/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj index d3db5f821da..7fa33b811c5 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj @@ -6,33 +6,19 @@ Library Microsoft.Graph.Authentication Microsoft.Graph.PowerShell.Authentication - true - false - ./bin - $(OutputPath) true true Microsoft.Graph.Authentication.nuspec © Microsoft Corporation. All rights reserved. - - - - - - - - - - - - - - + + + + @@ -47,7 +33,4 @@ Resources.Designer.cs - - - \ No newline at end of file diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec b/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec index 3d8d9b10de7..51bd046dba8 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec @@ -16,20 +16,20 @@ - - - - - - - - - - - - - - + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 index bd1b07d7582..2710468fb7d 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft # -# Generated on: 1/29/2021 +# Generated on: 3/2/2021 # @{ diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 index ea6bf11ddb6..63b8466b5d9 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 @@ -1,13 +1,3 @@ -# Load dependencies -$preloadPath = (Join-Path $PSScriptRoot -ChildPath ".\bin\PreloadAssemblies") -if ($PSEdition -eq 'Desktop' -and (Test-Path $preloadPath -ErrorAction Ignore)) { - try { - Get-ChildItem -ErrorAction Stop -Path $preloadPath -Filter "*.dll" | ForEach-Object { - Add-Type -Path $_.FullName -ErrorAction Ignore | Out-Null - } - } - catch { } -} # Load the module dll -$null = Import-Module -Name (Join-Path $PSScriptRoot '.\bin\Microsoft.Graph.Authentication.dll') \ No newline at end of file +$null = Import-Module -Name (Join-Path $PSScriptRoot 'Microsoft.Graph.Authentication.dll') \ No newline at end of file diff --git a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs new file mode 100644 index 00000000000..742a44ebb64 --- /dev/null +++ b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs @@ -0,0 +1,68 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) Microsoft Corporation. All Rights Reserved. Licensed under the MIT License. See License in the project root for license information. +// ------------------------------------------------------------------------------ + +namespace Microsoft.Graph.PowerShell.Authentication.Utilities +{ + using System; + using System.Collections.Generic; + using System.IO; + using System.Reflection; + public static class DependencyAssemblyResolver + { + // Catalog our dependencies here to ensure we don't load anything else. + private static IReadOnlyDictionary Dependencies = new Dictionary + { + { "Microsoft.Identity.Client", new Version("4.23.0.0") }, + { "Microsoft.Graph.Auth", new Version("1.0.0.0") }, + { "Microsoft.IdentityModel.Tokens", new Version("5.6.0.61018") }, + { "Microsoft.IdentityModel.Logging", new Version("5.6.0.61018") }, + { "Microsoft.IdentityModel.JsonWebTokens", new Version("5.6.0.61018") }, + { "System.IdentityModel.Tokens.Jwt", new Version("5.6.0.61018") }, + { "System.Security.Cryptography.ProtectedData", new Version("4.3.0.0") }, + { "Newtonsoft.Json", new Version("10.0.3.21018") }, + }; + + // Set up the path to our dependency directory within the module. + private static string DependenciesDirPath = Path.GetFullPath(Path.Combine( + Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Dependencies")); + + public static void Initialize() + { + Console.WriteLine($"Initialize"); + // Set up our event handler when the module is loaded. + AppDomain.CurrentDomain.AssemblyResolve += HandleResolveEvent; + } + + internal static void Reset() + { + Console.WriteLine($"Resting"); + // Remove our event hander when the module is unloaded. + AppDomain.CurrentDomain.AssemblyResolve -= HandleResolveEvent; + } + + private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) + { + try + { + AssemblyName assemblymName = new AssemblyName(args.Name); + Console.WriteLine($"Loading: {assemblymName.Name}"); + // We try to resolve our dependencies on our own. + if (Dependencies.TryGetValue(assemblymName.Name, out Version requiredVersion) + && requiredVersion >= assemblymName.Version + && (requiredVersion.Major == assemblymName.Version.Major || string.Equals(assemblymName.Name, "Newtonsoft.Json", StringComparison.OrdinalIgnoreCase))) + { + string requiredAssemblyPath = Path.Combine(DependenciesDirPath, $"{assemblymName.Name}.dll"); + Console.WriteLine($"Loading for engine assembly: {assemblymName.Name}"); + return Assembly.LoadFile(requiredAssemblyPath); + } + } catch + { + // If an error is encountered, we fall back to PowerShell's default dependency resolution. + } + + return null; + } + + } +} diff --git a/src/Authentication/Authentication/build-module.ps1 b/src/Authentication/Authentication/build-module.ps1 index e75aa4200cb..36d22385100 100644 --- a/src/Authentication/Authentication/build-module.ps1 +++ b/src/Authentication/Authentication/build-module.ps1 @@ -1,23 +1,45 @@ -param([switch]$Isolated, [switch]$Pack, [switch]$Release) +param( + [switch]$Isolated, + [switch]$Pack, + [switch]$Release +) + $ErrorActionPreference = 'Stop' +$ModuleName = "Authentication" +$ModulePrefix = "Microsoft.Graph" +$netStandard = "netstandard2.0" +$copyExtensions = @('.dll', '.pdb') + +# Source code locations +$coreSrc = Join-Path $PSScriptRoot "../$ModuleName.Core" +$cmdletsSrc = Join-Path $PSScriptRoot "../$ModuleName" + +# Generated output locations +$outDir = "$PSScriptRoot/artifacts" +$outDeps = "$outDir/Dependencies" -if($PSEdition -ne 'Core') { +$Configuration = 'Debug' +if ($Release) { + $Configuration = 'Release' +} + +if ($PSEdition -ne 'Core') { Write-Error 'This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell.' } -if(-not $Isolated) { +if (-not $Isolated) { Write-Host -ForegroundColor Green 'Creating isolated process...' $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path & "$pwsh" -NonInteractive -NoLogo -NoProfile -File $MyInvocation.MyCommand.Path @PSBoundParameters -Isolated - if($LastExitCode -ne 0) { + if ($LastExitCode -ne 0) { # Build failed. Don't attempt to run the module. return } - if($Pack) { + if ($Pack) { . (Join-Path $PSScriptRoot 'pack-module.ps1') - if($LastExitCode -ne 0) { + if ($LastExitCode -ne 0) { # Packing failed. Don't attempt to run the module. return } @@ -26,26 +48,54 @@ if(-not $Isolated) { return } +# Clean build folders. Write-Host -ForegroundColor Green 'Cleaning build folders...' -$binFolder = Join-Path $PSScriptRoot 'bin' -$objFolder = Join-Path $PSScriptRoot 'obj' -$null = Remove-Item -Recurse -ErrorAction SilentlyContinue -Path $binFolder, $objFolder +$null = Remove-Item -Path "$coreSrc/bin", "$coreSrc/obj" -Recurse -ErrorAction Ignore +$null = Remove-Item -Path "$cmdletsSrc/bin", "$cmdletsSrc/obj" -Recurse -ErrorAction Ignore -if((Test-Path $binFolder) -or (Test-Path $objFolder)) { +if ((Test-Path "$cmdletsSrc/bin") -or (Test-Path "$cmdletsSrc/obj")) { Write-Host -ForegroundColor Cyan 'Did you forget to exit your isolated module session before rebuilding?' Write-Error 'Unable to clean ''bin'' or ''obj'' folder. A process may have an open handle.' } Write-Host -ForegroundColor Green 'Compiling module...' -$buildConfig = 'Debug' -if($Release) { - $buildConfig = 'Release' -} -dotnet publish $PSScriptRoot --verbosity quiet --configuration $buildConfig /nologo +# Build Authentication.Core +Push-Location $coreSrc +dotnet publish -c $Configuration --verbosity quiet /nologo +Pop-Location -if($LastExitCode -ne 0) { +# Build Authentication +Push-Location $cmdletsSrc +dotnet publish -c $Configuration --verbosity quiet /nologo +Pop-Location + +if ($LastExitCode -ne 0) { Write-Error 'Compilation failed.' } -$null = Remove-Item -Recurse -ErrorAction SilentlyContinue -Path (Join-Path $binFolder 'Debug'), (Join-Path $binFolder 'Release') +# Ensure out directory exists and is clean +Remove-Item -Path $outDir -Recurse -ErrorAction Ignore +New-Item -Path $outDir -ItemType Directory +New-Item -Path $outDeps -ItemType Directory + +# Copy manifest. +Copy-Item -Path "$cmdletsSrc/$ModulePrefix.$ModuleName.format.ps1xml" -Destination $outDir +Copy-Item -Path "$cmdletsSrc/$ModulePrefix.$ModuleName.psm1" -Destination $outDir +Copy-Item -Path "$cmdletsSrc/$ModulePrefix.$ModuleName.psd1" -Destination $outDir + +# Core assemblies to include with cmdlets (Let PowerShell load them). +$CoreAssemblies = @('Microsoft.Graph.Authentication.Core', 'Microsoft.Graph.Core') + +# Copy each core asset and remember it. +$Deps = [System.Collections.Generic.HashSet[string]]::new() +Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netStandard/publish/" | + Where-Object { $_.Extension -in $copyExtensions } | + Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | + ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outDeps } + +# Now copy each Cmdlets asset, not taking any found in Engine. +Get-ChildItem -Path "$cmdletsSrc/bin/$Configuration/$netStandard/publish/" | + Where-Object { -not $Deps.Contains($_.Name) -and $_.Extension -in $copyExtensions } | + ForEach-Object { Copy-Item -Path $_.FullName -Destination $outDir } + Write-Host -ForegroundColor Green '-------------Done-------------' \ No newline at end of file From 8c66c29717bfc7977ef239801148eaf5bfcefada Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 2 Mar 2021 15:20:27 -0800 Subject: [PATCH 02/10] Update build scripts to build, sign and pack auth module with core dll. --- tools/BuildModule.ps1 | 27 +++++++++-------------- tools/CSProjHelper.ps1 | 30 ++++++++++++++++++++++++++ tools/GenerateAuthenticationModule.ps1 | 21 ++++++++++++------ tools/PackModule.ps1 | 15 +++++++------ 4 files changed, 63 insertions(+), 30 deletions(-) create mode 100644 tools/CSProjHelper.ps1 diff --git a/tools/BuildModule.ps1 b/tools/BuildModule.ps1 index 74bd7688034..6a4240fad56 100644 --- a/tools/BuildModule.ps1 +++ b/tools/BuildModule.ps1 @@ -16,6 +16,7 @@ if ($PSEdition -ne "Core") { } $NuspecHelperPS1 = Join-Path $PSScriptRoot "./NuspecHelper.ps1" +$CSProjHelperPS1 = Join-Path $PSScriptRoot "./CSProjHelper.ps1" $ModuleProjLocation = Join-Path $PSScriptRoot "../src/$Module/$Module" $BuildModulePS1 = Join-Path $ModuleProjLocation "/build-module.ps1" $ModuleCsProj = Join-Path $ModuleProjLocation "$ModulePrefix.$Module.csproj" @@ -25,27 +26,19 @@ $ModuleNuspec = Join-Path $ModuleProjLocation "$ModulePrefix.$Module.nuspec" # Import scripts . $NuspecHelperPS1 +. $CSProjHelperPS1 if (-not (Test-Path -Path $BuildModulePS1)) { Write-Error "Build script file '$BuildModulePS1' not found for '$Module' module." } # Set delay sign to true. - -$ModuleProjDoc = New-Object System.Xml.XmlDocument -$ModuleProjDoc.Load($ModuleCsProj) -$ModuleProjElement = [System.Xml.XmlElement] $ModuleProjDoc.DocumentElement.FirstChild if ($EnableSigning) { - Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "AssemblyOriginatorKeyFile" -ElementValue (Join-Path $PSScriptRoot $NuspecMetadata["assemblyOriginatorKeyFile"]) - Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "DelaySign" -ElementValue "true" - Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "SignAssembly" -ElementValue "true" + Set-CSProjValues -ModuleCsProj $ModuleCsProj -ModuleVersion $ModuleVersion -AssemblyOriginatorKeyFile $NuspecMetadata["assemblyOriginatorKeyFile"] +} +else { + Set-CSProjValues -ModuleCsProj $ModuleCsProj -ModuleVersion $ModuleVersion -Copyright $NuspecMetadata["copyright"] } -Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "Copyright" -ElementValue $NuspecMetadata["copyright"] -Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "Version" -ElementValue $ModuleVersion - -$ModuleProjDoc.Save($ModuleCsProj) -Write-Host "Updated the .csproj." - # Build module Write-Host -ForegroundColor Green "Building '$Module' module..." @@ -55,10 +48,10 @@ if ($LASTEXITCODE) { } [HashTable]$ModuleManifestSettings = @{ - Path = $ModuleManifest - ModuleVersion = $ModuleVersion - IconUri = $NuspecMetadata["iconUri"] - ReleaseNotes = $ReleaseNotes + Path = $ModuleManifest + ModuleVersion = $ModuleVersion + IconUri = $NuspecMetadata["iconUri"] + ReleaseNotes = $ReleaseNotes } $FullVersionNumber = $ModuleVersion diff --git a/tools/CSProjHelper.ps1 b/tools/CSProjHelper.ps1 new file mode 100644 index 00000000000..aada431fba1 --- /dev/null +++ b/tools/CSProjHelper.ps1 @@ -0,0 +1,30 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +function Set-CSProjValues( + [parameter(Mandatory = $true)][string] $ModuleCsProj, + [parameter(Mandatory = $true)][string] $ModuleVersion, + [string] $Copyright, + [string] $AssemblyOriginatorKeyFile) { + $NuspecHelperPS1 = Join-Path $PSScriptRoot "./NuspecHelper.ps1" + + # Import scripts + . $NuspecHelperPS1 + + # Set delay sign to true. + $ModuleProjDoc = New-Object System.Xml.XmlDocument + $ModuleProjDoc.Load($ModuleCsProj) + $ModuleProjElement = [System.Xml.XmlElement] $ModuleProjDoc.DocumentElement.PropertyGroup + if (![string]::IsNullOrWhiteSpace($AssemblyOriginatorKeyFile)) { + Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "AssemblyOriginatorKeyFile" -ElementValue (Join-Path $PSScriptRoot $AssemblyOriginatorKeyFile) + Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "DelaySign" -ElementValue "true" + Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "SignAssembly" -ElementValue "true" + } + if (![string]::IsNullOrWhiteSpace($Copyright)) { + Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "Copyright" -ElementValue $Copyright + } + Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "Version" -ElementValue $ModuleVersion + + $ModuleProjDoc.Save($ModuleCsProj) + Write-Host "Updated the $ModuleCsProj." +} \ No newline at end of file diff --git a/tools/GenerateAuthenticationModule.ps1 b/tools/GenerateAuthenticationModule.ps1 index abd5de076b8..327058de407 100644 --- a/tools/GenerateAuthenticationModule.ps1 +++ b/tools/GenerateAuthenticationModule.ps1 @@ -27,12 +27,19 @@ if ($PSEdition -ne 'Core') { $ModulePrefix = "Microsoft.Graph" $ModuleName = "Authentication" $AuthModuleManifest = "Microsoft.Graph.Authentication.psd1" +$SigningKeyFile = "35MSSharedLib1024.snk" $BuildModulePS1 = Join-Path $PSScriptRoot ".\BuildModule.ps1" -Resolve $PackModulePS1 = Join-Path $PSScriptRoot ".\PackModule.ps1" -Resolve $PublishModulePS1 = Join-Path $PSScriptRoot ".\PublishModule.ps1" -Resolve $ValidateUpdatedModuleVersionPS1 = Join-Path $PSScriptRoot ".\ValidateUpdatedModuleVersion.ps1" -Resolve -$AuthModulePath = Join-Path $PSScriptRoot "..\src\Authentication\Authentication\" -Resolve +$AuthSrcPath = Join-Path $PSScriptRoot "..\src\Authentication\" +$AuthModulePath = Join-Path $AuthSrcPath "Authentication" -Resolve $TestModulePS1 = Join-Path $PSScriptRoot ".\TestModule.ps1" -Resolve +$AuthCoreCSProj = Join-Path $AuthSrcPath "$ModuleName.Core" "$ModulePrefix.$ModuleName.Core.csproj" +$CSProjHelperPS1 = Join-Path $PSScriptRoot "./CSProjHelper.ps1" + +# Import scripts +. $CSProjHelperPS1 # Read ModuleVersion set on local auth module. $ManifestContent = Import-LocalizedData -BaseDirectory $AuthModulePath -FileName $AuthModuleManifest @@ -41,8 +48,8 @@ if ($null -eq $ManifestContent.ModuleVersion) { Write-Error "Version number is not set on $ModulePrefix.$ModuleName module. Please set 'ModuleVersion' in $AuthModulePath\$AuthModuleManifest." } $AllowPreRelease = $true -if($ModulePreviewNumber -eq -1) { - $AllowPreRelease = $false +if ($ModulePreviewNumber -eq -1) { + $AllowPreRelease = $false } # Validate module version with the one on PSGallery. [VersionState]$VersionState = & $ValidateUpdatedModuleVersionPS1 -ModuleName "$ModulePrefix.$ModuleName" -NextVersion $ManifestContent.ModuleVersion -PSRepository $RepositoryName -ModulePreviewNumber $ModulePreviewNumber @@ -54,18 +61,20 @@ elseif ($VersionState.Equals([VersionState]::EqualToFeed) -and !$BuildWhenEqual) Write-Warning "$ModulePrefix.$ModuleName module skipped. Version has not changed and is equal to what's on $RepositoryName." } elseif ($VersionState.Equals([VersionState]::Valid) -or $VersionState.Equals([VersionState]::NotOnFeed) -or $BuildWhenEqual) { - $ModuleVersion = $VersionState.Equals([VersionState]::NotOnFeed) ? "0.1.1" : $ManifestContent.ModuleVersion + $ModuleVersion = $VersionState.Equals([VersionState]::NotOnFeed) ? "1.0.0" : $ManifestContent.ModuleVersion # Build and pack generated module. if ($Build) { if ($EnableSigning) { + Set-CSProjValues -ModuleCsProj $AuthCoreCSProj -ModuleVersion $ModuleVersion -AssemblyOriginatorKeyFile $SigningKeyFile & $BuildModulePS1 -Module $ModuleName -ModulePrefix $ModulePrefix -ModuleVersion $ModuleVersion -ModulePreviewNumber $ModulePreviewNumber -ReleaseNotes $ManifestContent.PrivateData.PSData.ReleaseNotes -EnableSigning } else { + Set-CSProjValues -ModuleCsProj $AuthCoreCSProj -ModuleVersion $ModuleVersion & $BuildModulePS1 -Module $ModuleName -ModulePrefix $ModulePrefix -ModuleVersion $ModuleVersion -ModulePreviewNumber $ModulePreviewNumber -ReleaseNotes $ManifestContent.PrivateData.PSData.ReleaseNotes } } - if($Test){ - & $TestModulePS1 -ModulePath $AuthModulePath -ModuleName "$ModulePrefix.$ModuleName" + if ($Test) { + & $TestModulePS1 -ModulePath $AuthModulePath -ModuleName "$ModulePrefix.$ModuleName" } if ($Pack) { diff --git a/tools/PackModule.ps1 b/tools/PackModule.ps1 index 820ebc6b899..b29c7c00b44 100644 --- a/tools/PackModule.ps1 +++ b/tools/PackModule.ps1 @@ -3,7 +3,7 @@ Param( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()][string] $Module, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()][string] $ArtifactsLocation, - [string] $ModulePrefix="Microsoft.Graph", + [string] $ModulePrefix = "Microsoft.Graph", [switch] $ExcludeMarkdownDocsFromNugetPackage ) $NuspecHelperPS1 = Join-Path $PSScriptRoot "./NuspecHelper.ps1" @@ -12,8 +12,8 @@ $NuspecHelperPS1 = Join-Path $PSScriptRoot "./NuspecHelper.ps1" $LASTEXITCODE = $null $ErrorActionPreference = "Stop" -if($PSEdition -ne "Core") { - Write-Error "This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell." +if ($PSEdition -ne "Core") { + Write-Error "This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell." } $ModuleProjLocation = Join-Path $PSScriptRoot "../src/$Module/$Module" @@ -28,17 +28,18 @@ if (Test-Path $PackModulePS1) { } # Pack module & $PackModulePS1 - if($LASTEXITCODE) { + if ($LASTEXITCODE) { Write-Error "Failed to pack '$Module' module." } # Get generated .nupkg - $NuGetPackage = (Get-ChildItem (Join-Path $ModuleProjLocation "./bin") | Where-Object Name -Match ".nupkg").FullName + $NuGetPackage = (Get-ChildItem (Join-Path $ModuleProjLocation "./bin") -Recurse | Where-Object Name -Match ".nupkg").FullName $ModuleArtifactLocation = "$ArtifactsLocation\$Module" - if(-not (Test-Path $ModuleArtifactLocation)) { + if (-not (Test-Path $ModuleArtifactLocation)) { New-Item -Path $ModuleArtifactLocation -Type Directory - } else { + } + else { Remove-Item -Path "$ModuleArtifactLocation\*" -Recurse -Force } From 6e39e5a6de1046ac0c80988845844f245098d743 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 2 Mar 2021 15:43:11 -0800 Subject: [PATCH 03/10] Updates ADO pipeline to also sign and strong name MSG.Authentication.Core.dll. --- .azure-pipelines/generate-auth-module-template.yml | 14 ++++++++------ .azure-pipelines/generate-modules-template.yml | 12 ++++++------ .../generate-service-modules.yml | 11 ++++++----- .azure-pipelines/integrated-pipeline.yml | 4 ---- .azure-pipelines/weekly-generation.yml | 3 +-- 5 files changed, 21 insertions(+), 23 deletions(-) diff --git a/.azure-pipelines/generate-auth-module-template.yml b/.azure-pipelines/generate-auth-module-template.yml index 51c36bb6355..628b4947c51 100644 --- a/.azure-pipelines/generate-auth-module-template.yml +++ b/.azure-pipelines/generate-auth-module-template.yml @@ -10,10 +10,6 @@ parameters: displayName: 'Authentication Module Name' type: string default: 'Authentication' - - name: AUTH_MODULE_DLL_PATTERN - displayName: 'Authentication Module DLL Pattern' - type: string - default: 'Microsoft.Graph.Authentication.dll' - name: Api_Key displayName: 'Api Key' type: string @@ -79,7 +75,10 @@ jobs: inputs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) - Pattern: $(AUTH_MODULE_DLL_PATTERN) + # Recursively finds files matching these patterns: + Pattern: | + **/Microsoft.Graph.Authentication.dll + **/Microsoft.Graph.Authentication.Core.dll signConfigType: inlineSignParams inlineOperation: | [ @@ -106,7 +105,10 @@ jobs: inputs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) - Pattern: $(AUTH_MODULE_DLL_PATTERN) + # Recursively finds files matching these patterns: + Pattern: | + **/Microsoft.Graph.Authentication.dll + **/Microsoft.Graph.Authentication.Core.dll signConfigType: inlineSignParams inlineOperation: | [ diff --git a/.azure-pipelines/generate-modules-template.yml b/.azure-pipelines/generate-modules-template.yml index 4438d612e71..936151e253d 100644 --- a/.azure-pipelines/generate-modules-template.yml +++ b/.azure-pipelines/generate-modules-template.yml @@ -6,10 +6,6 @@ parameters: displayName: 'Module Path' type: string default: 'src\Authentication\Authentication\bin\' - - name: AUTH_MODULE_DLL_PATTERN - displayName: 'Module Pattern' - type: string - default: 'Microsoft.Graph.Authentication.dll' - name: Api_Key displayName: 'Api Key' type: string @@ -67,7 +63,9 @@ jobs: inputs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) - Pattern: '$(AUTH_MODULE_DLL_PATTERN)' + Pattern: | + **/Microsoft.Graph.Authentication.dll + **/Microsoft.Graph.Authentication.Core.dll signConfigType: inlineSignParams inlineOperation: | [ @@ -94,7 +92,9 @@ jobs: inputs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) - Pattern: $(AUTH_MODULE_DLL_PATTERN) + Pattern: | + **/Microsoft.Graph.Authentication.dll + **/Microsoft.Graph.Authentication.Core.dll signConfigType: inlineSignParams inlineOperation: | [ diff --git a/.azure-pipelines/generation-templates/generate-service-modules.yml b/.azure-pipelines/generation-templates/generate-service-modules.yml index e0386e99079..434aaa535b2 100644 --- a/.azure-pipelines/generation-templates/generate-service-modules.yml +++ b/.azure-pipelines/generation-templates/generate-service-modules.yml @@ -6,8 +6,6 @@ parameters: type: string - name: AuthModulePath type: string - - name: AuthModuleDllPattern - type: string - name: ServiceModulePath type: string - name: ModulePrefix @@ -30,7 +28,6 @@ jobs: Branch: ${{ parameters.Branch }} ModulesToGenerate: ${{ parameters.ModulesToGenerate }} AuthModulePath: ${{ parameters.AuthModulePath }} - AuthModuleDllPattern: ${{ parameters.AuthModuleDllPattern }} ServiceModulePath: ${{ parameters.ServiceModulePath }} ModulePrefix: ${{ parameters.ModulePrefix }} EnableSigning: ${{ parameters.EnableSigning }} @@ -64,7 +61,9 @@ jobs: inputs: ConnectedServiceName: "microsoftgraph ESRP CodeSign DLL and NuGet (AKV)" FolderPath: $(AuthModulePath) - Pattern: $(AuthModuleDllPattern) + Pattern: | + **/Microsoft.Graph.Authentication.dll + **/Microsoft.Graph.Authentication.Core.dll signConfigType: inlineSignParams inlineOperation: | [ @@ -91,7 +90,9 @@ jobs: inputs: ConnectedServiceName: "microsoftgraph ESRP CodeSign DLL and NuGet (AKV)" FolderPath: $(AuthModulePath) - Pattern: $(AuthModuleDllPattern) + Pattern: | + **/Microsoft.Graph.Authentication.dll + **/Microsoft.Graph.Authentication.Core.dll signConfigType: inlineSignParams inlineOperation: | [ diff --git a/.azure-pipelines/integrated-pipeline.yml b/.azure-pipelines/integrated-pipeline.yml index 2aa5247707b..b79aa29fdde 100644 --- a/.azure-pipelines/integrated-pipeline.yml +++ b/.azure-pipelines/integrated-pipeline.yml @@ -4,8 +4,6 @@ variables: AUTH_MODULE_NAME: 'Authentication' AUTH_MODULE_PATH: 'src\Authentication\Authentication' - AUTH_MODULE_DLL_PATTERN: 'Microsoft.Graph.Authentication.dll' - MODULE_DLL_PATTERN: 'Microsoft.Graph.Authentication.dll' MODULE_PREFIX: 'Microsoft.Graph' ROLLUP_MODULE_NAME: 'Graph' ROLLUP_MODULE_PATH: 'src/Graph/Graph' @@ -85,7 +83,6 @@ stages: Api_Key: $(Api_Key) AUTH_MODULE_NAME: $(AUTH_MODULE) AUTH_MODULE_PATH: $(AUTH_MODULE_PATH) - AUTH_MODULE_DLL_PATTERN: $(AUTH_MODULE_DLL_PATTERN) EnableSigning: true BUILDNUMBER: $(BUILDNUMBER) @@ -99,7 +96,6 @@ stages: WORKLOAD_MODULE_PATH: $(WORKLOAD_MODULE_PATH) GRAPH_VERSION: $(GRAPH_VERSION) AUTH_MODULE_PATH: $(AUTH_MODULE_PATH) - AUTH_MODULE_DLL_PATTERN: $(AUTH_MODULE_DLL_PATTERN) EnableSigning: true BUILDNUMBER: $(BUILDNUMBER) diff --git a/.azure-pipelines/weekly-generation.yml b/.azure-pipelines/weekly-generation.yml index eb9951d2780..649e8e65e92 100644 --- a/.azure-pipelines/weekly-generation.yml +++ b/.azure-pipelines/weekly-generation.yml @@ -33,8 +33,7 @@ stages: parameters: ModulesToGenerate: $[ stageDependencies.DownloadOpenAPIDocs.GetLatestDocs.outputs['OpenAPIDocDiff.ModulesWithChanges'] ] Branch: $[ stageDependencies.DownloadOpenAPIDocs.GetLatestDocs.outputs['ComputeBranch.WeeklyBranch'] ] - AuthModulePath: "src/Authentication/Authentication/bin/" - AuthModuleDllPattern: "Microsoft.Graph.Authentication.dll" + AuthModulePath: "src/Authentication/Authentication" ServiceModulePath: "src/" ModulePrefix: "Microsoft.Graph" EnableSigning: false From cef547409e4fe399f11ee9587c00170cfb6e2cb2 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Thu, 11 Mar 2021 11:09:34 -0800 Subject: [PATCH 04/10] Add Pester tests for loading module side by side. --- .../Authentication/test/Connect-MgGraph.Tests.ps1 | 13 ++++++++++++- .../test/Invoke-MgGraphRequest.Tests.ps1 | 3 ++- tools/GenerateAuthenticationModule.ps1 | 2 +- tools/GenerateModules.ps1 | 2 +- tools/TestModule.ps1 | 9 ++++----- 5 files changed, 20 insertions(+), 9 deletions(-) diff --git a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 index 762045ba529..2ef6d295c88 100644 --- a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 +++ b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 @@ -1,6 +1,6 @@ BeforeAll { $ModuleName = "Microsoft.Graph.Authentication" - $ModulePath = Join-Path $PSScriptRoot "..\$ModuleName.psd1" + $ModulePath = Join-Path $PSScriptRoot "..\artifacts\$ModuleName.psd1" Import-Module $ModulePath -Force $RandomClientId = (New-Guid).Guid } @@ -22,4 +22,15 @@ Describe 'Connect-MgGraph In App Mode' { It 'ShouldThrowExceptionWhenCertificateThumbprintLengthIs < 40' { { Connect-MgGraph -ClientId $RandomClientId -CertificateThumbprint '123456789012345678901234567890123456789' -ErrorAction Stop } | Should -Throw -ExpectedMessage "*'CertificateThumbprint' must have a length of 40.*" } + +} +Describe 'Connect-MgGraph Depencency Resolution' { + BeforeAll { + Install-Module Az.Accounts -Repository PSGallery -Scope CurrentUser -Force + } + + It 'ShouldSuccessfullyLoadMgModuleSideBySideWithAzModule.' { + { Connect-AzAccount -ApplicationId $RandomClientId -CertificateThumbprint "Invalid" -Tenant "Invalid" -ErrorAction Stop } | Should -Throw -ExpectedMessage "*Could not find tenant id*" + { Connect-MgGraph -Scopes "inavid.scope" -ErrorAction Stop } | Should -Throw -ExpectedMessage "*AADSTS70011:*" + } } \ No newline at end of file diff --git a/src/Authentication/Authentication/test/Invoke-MgGraphRequest.Tests.ps1 b/src/Authentication/Authentication/test/Invoke-MgGraphRequest.Tests.ps1 index 0d643681ea9..b3a90dcd091 100644 --- a/src/Authentication/Authentication/test/Invoke-MgGraphRequest.Tests.ps1 +++ b/src/Authentication/Authentication/test/Invoke-MgGraphRequest.Tests.ps1 @@ -5,8 +5,9 @@ } . ($loadEnvPath) $ModuleName = "Microsoft.Graph.Authentication" - $ModulePath = Join-Path $PSScriptRoot "..\$ModuleName.psd1" + $ModulePath = Join-Path $PSScriptRoot "..\artifacts\$ModuleName.psd1" Import-Module $ModulePath -Force + $PSDefaultParameterValues=@{"Connect-MgGraph:TenantId"=${env:TENANTIDENTIFIER}; "Connect-MgGraph:ClientId"=${env:CLIENTIDENTIFIER}; "Connect-MgGraph:CertificateThumbprint"=${env:CERTIFICATETHUMBPRINT}} } Describe 'Invoke-MgGraphRequest Collection Results' { BeforeAll { diff --git a/tools/GenerateAuthenticationModule.ps1 b/tools/GenerateAuthenticationModule.ps1 index 327058de407..2d088ec9967 100644 --- a/tools/GenerateAuthenticationModule.ps1 +++ b/tools/GenerateAuthenticationModule.ps1 @@ -74,7 +74,7 @@ elseif ($VersionState.Equals([VersionState]::Valid) -or $VersionState.Equals([Ve } } if ($Test) { - & $TestModulePS1 -ModulePath $AuthModulePath -ModuleName "$ModulePrefix.$ModuleName" + & $TestModulePS1 -ModulePath (Join-Path $AuthModulePath "artifacts" ) -ModuleName "$ModulePrefix.$ModuleName" -ModuleTestsPath (Join-Path $AuthModulePath "test") } if ($Pack) { diff --git a/tools/GenerateModules.ps1 b/tools/GenerateModules.ps1 index f802d563ab1..1ca641ab7cb 100644 --- a/tools/GenerateModules.ps1 +++ b/tools/GenerateModules.ps1 @@ -202,7 +202,7 @@ $ModulesToGenerate | ForEach-Object -ThrottleLimit $ModulesToGenerate.Count -Par } if ($Using:Test) { - & $Using:TestModulePS1 -ModulePath $ModuleProjectDir -ModuleName $FullyQualifiedModuleName + & $Using:TestModulePS1 -ModulePath $ModuleProjectDir -ModuleName $FullyQualifiedModuleName -ModuleTestsPath (Join-Path $ModuleProjectDir "test") } if ($Using:Pack) { diff --git a/tools/TestModule.ps1 b/tools/TestModule.ps1 index dd01070afbc..284b735a799 100644 --- a/tools/TestModule.ps1 +++ b/tools/TestModule.ps1 @@ -1,6 +1,6 @@ # Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. -param([string] $ModulePath, [string] $ModuleName, [switch]$Isolated) +param([string] $ModulePath, [string] $ModuleName, [string] $ModuleTestsPath, [switch]$Isolated) $ErrorActionPreference = 'Stop' # Install Pester @@ -22,15 +22,14 @@ Import-Module -Name Pester Import-Module -Name $modulePsd1.FullName # Replace AutoREST loadEnv.ps1 with our local scipt. -Copy-Item -Path $LocalLoadEnvPS1 -Destination "$ModulePath/test" +Copy-Item -Path $LocalLoadEnvPS1 -Destination $ModuleTestsPath -$testFolder = Join-Path $ModulePath 'test' $PesterConfiguration = [PesterConfiguration]::Default -$PesterConfiguration.Run.Path = $testFolder +$PesterConfiguration.Run.Path = $ModuleTestsPath $PesterConfiguration.Run.Exit = $true $PesterConfiguration.CodeCoverage.Enabled = $true $PesterConfiguration.TestResult.Enabled = $true -$PesterConfiguration.TestResult.OutputPath = (Join-Path $testFolder "$moduleName-TestResults.xml") +$PesterConfiguration.TestResult.OutputPath = (Join-Path $ModuleTestsPath "$moduleName-TestResults.xml") try { Invoke-Pester -Configuration $PesterConfiguration From c5fcb1b07357bf3aeccf7e0cc2577fde980cd314 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Thu, 11 Mar 2021 14:38:56 -0800 Subject: [PATCH 05/10] Add internals visible to test proj. --- .../Authentication.Core/Properties/AssemblyInfo.cs | 5 +++++ .../Authentication.Test/Helpers/GraphSessionTests.cs | 2 +- .../Authentication/test/Connect-MgGraph.Tests.ps1 | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 src/Authentication/Authentication.Core/Properties/AssemblyInfo.cs diff --git a/src/Authentication/Authentication.Core/Properties/AssemblyInfo.cs b/src/Authentication/Authentication.Core/Properties/AssemblyInfo.cs new file mode 100644 index 00000000000..47dc954fb32 --- /dev/null +++ b/src/Authentication/Authentication.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,5 @@ +using System.Runtime.CompilerServices; + +#if DEBUG +[assembly: InternalsVisibleTo("Microsoft.Graph.Authentication.Test")] +#endif diff --git a/src/Authentication/Authentication.Test/Helpers/GraphSessionTests.cs b/src/Authentication/Authentication.Test/Helpers/GraphSessionTests.cs index 2bb665e8ee8..cbc0ae6456e 100644 --- a/src/Authentication/Authentication.Test/Helpers/GraphSessionTests.cs +++ b/src/Authentication/Authentication.Test/Helpers/GraphSessionTests.cs @@ -52,7 +52,7 @@ public void ShouldThrowExceptionWhenSessionIsNotInitialized() { InvalidOperationException exception = Assert.Throws(() => GraphSession.Instance); - Assert.Equal(ErrorConstants.Codes.SessionNotInitialized, exception.Message); + Assert.Equal(PowerShell.Authentication.Core.ErrorConstants.Codes.SessionNotInitialized, exception.Message); // reset static instance. GraphSession.Reset(); diff --git a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 index 2ef6d295c88..f95685d3534 100644 --- a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 +++ b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 @@ -29,7 +29,7 @@ Describe 'Connect-MgGraph Depencency Resolution' { Install-Module Az.Accounts -Repository PSGallery -Scope CurrentUser -Force } - It 'ShouldSuccessfullyLoadMgModuleSideBySideWithAzModule.' { + It 'ShouldLoadMgModuleSideBySideWithAzModule.' { { Connect-AzAccount -ApplicationId $RandomClientId -CertificateThumbprint "Invalid" -Tenant "Invalid" -ErrorAction Stop } | Should -Throw -ExpectedMessage "*Could not find tenant id*" { Connect-MgGraph -Scopes "inavid.scope" -ErrorAction Stop } | Should -Throw -ExpectedMessage "*AADSTS70011:*" } From cc8d5253b57ce0c846429a28133fd3697fe1841a Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Thu, 11 Mar 2021 17:58:42 -0800 Subject: [PATCH 06/10] Load multiframework dependencies. --- ...Microsoft.Graph.Authentication.Core.csproj | 11 ++---- .../Authentication/Cmdlets/ConnectMgGraph.cs | 1 - .../Microsoft.Graph.Authentication.psm1 | 9 ++++- .../InitializeAssemblyResolver.ps1 | 11 ++++++ .../Utilities/DependencyAssemblyResolver.cs | 34 ++++++++++++++++--- .../Authentication/build-module.ps1 | 33 ++++++++++++++---- 6 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 src/Authentication/Authentication/StartupScripts/InitializeAssemblyResolver.ps1 diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index 0b3d23b50e0..97caafea8f7 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -1,11 +1,9 @@ - + - - netstandard2.0 + netstandard2.0;netcoreapp2.1;net461 Microsoft.Graph.PowerShell.Authentication.Core - @@ -18,13 +16,10 @@ - - - - + \ No newline at end of file diff --git a/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs b/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs index 218613199f1..4de46081237 100644 --- a/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs +++ b/src/Authentication/Authentication/Cmdlets/ConnectMgGraph.cs @@ -291,7 +291,6 @@ private void ValidateParameters() /// public void OnImport() { - DependencyAssemblyResolver.Initialize(); GraphSessionInitializer.InitializeSession(); GraphSession.Instance.DataStore = new DiskDataStore(); } diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 index 63b8466b5d9..2dd0e3787f2 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psm1 @@ -1,3 +1,10 @@ # Load the module dll -$null = Import-Module -Name (Join-Path $PSScriptRoot 'Microsoft.Graph.Authentication.dll') \ No newline at end of file +$null = Import-Module -Name (Join-Path $PSScriptRoot 'Microsoft.Graph.Authentication.dll') + +if (Test-Path -Path "$PSScriptRoot\StartupScripts" -ErrorAction Ignore) +{ + Get-ChildItem "$PSScriptRoot\StartupScripts" -ErrorAction Stop | ForEach-Object { + . $_.FullName + } +} \ No newline at end of file diff --git a/src/Authentication/Authentication/StartupScripts/InitializeAssemblyResolver.ps1 b/src/Authentication/Authentication/StartupScripts/InitializeAssemblyResolver.ps1 new file mode 100644 index 00000000000..4926d2271d4 --- /dev/null +++ b/src/Authentication/Authentication/StartupScripts/InitializeAssemblyResolver.ps1 @@ -0,0 +1,11 @@ +try { + if ($PSEdition -eq 'Core') { + [Microsoft.Graph.PowerShell.Authentication.Utilities.DependencyAssemblyResolver]::Initialize() + } + else { + [Microsoft.Graph.PowerShell.Authentication.Utilities.DependencyAssemblyResolver]::Initialize($true) + } +} +catch { + Write-Warning $_ +} \ No newline at end of file diff --git a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs index 742a44ebb64..16f4719f887 100644 --- a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs +++ b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs @@ -23,12 +23,27 @@ public static class DependencyAssemblyResolver { "Newtonsoft.Json", new Version("10.0.3.21018") }, }; + private static IList MultiFrameworkDependencies = new List { + "Microsoft.Identity.Client", + "System.Security.Cryptography.ProtectedData" + }; + // Set up the path to our dependency directory within the module. private static string DependenciesDirPath = Path.GetFullPath(Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Dependencies")); - public static void Initialize() + private static string FrameworkDependenciesDirPath; + + public static void Initialize(bool isDesktopEdition = false) { + if (isDesktopEdition) + { + FrameworkDependenciesDirPath = "Desktop"; + } + else + { + FrameworkDependenciesDirPath = "Core"; + } Console.WriteLine($"Initialize"); // Set up our event handler when the module is loaded. AppDomain.CurrentDomain.AssemblyResolve += HandleResolveEvent; @@ -45,6 +60,9 @@ private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) { try { + string ver = Assembly.GetEntryAssembly()?.GetCustomAttribute()?.FrameworkName; + Console.WriteLine($".NET Version: {ver}"); + AssemblyName assemblymName = new AssemblyName(args.Name); Console.WriteLine($"Loading: {assemblymName.Name}"); // We try to resolve our dependencies on our own. @@ -52,17 +70,25 @@ private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) && requiredVersion >= assemblymName.Version && (requiredVersion.Major == assemblymName.Version.Major || string.Equals(assemblymName.Name, "Newtonsoft.Json", StringComparison.OrdinalIgnoreCase))) { - string requiredAssemblyPath = Path.Combine(DependenciesDirPath, $"{assemblymName.Name}.dll"); + string requiredAssemblyPath = string.Empty; + if (MultiFrameworkDependencies.Contains(assemblymName.Name)) + { + requiredAssemblyPath = Path.Combine(DependenciesDirPath, FrameworkDependenciesDirPath, $"{assemblymName.Name}.dll"); + } + else + { + requiredAssemblyPath = Path.Combine(DependenciesDirPath, $"{assemblymName.Name}.dll"); + } Console.WriteLine($"Loading for engine assembly: {assemblymName.Name}"); return Assembly.LoadFile(requiredAssemblyPath); } - } catch + } + catch { // If an error is encountered, we fall back to PowerShell's default dependency resolution. } return null; } - } } diff --git a/src/Authentication/Authentication/build-module.ps1 b/src/Authentication/Authentication/build-module.ps1 index 36d22385100..d2dfa0e19f2 100644 --- a/src/Authentication/Authentication/build-module.ps1 +++ b/src/Authentication/Authentication/build-module.ps1 @@ -8,6 +8,8 @@ $ErrorActionPreference = 'Stop' $ModuleName = "Authentication" $ModulePrefix = "Microsoft.Graph" $netStandard = "netstandard2.0" +$netCoreApp = "netcoreapp2.1" +$netFx = "net461" $copyExtensions = @('.dll', '.pdb') # Source code locations @@ -17,6 +19,8 @@ $cmdletsSrc = Join-Path $PSScriptRoot "../$ModuleName" # Generated output locations $outDir = "$PSScriptRoot/artifacts" $outDeps = "$outDir/Dependencies" +$outCore = "$outDeps/Core" +$outDesktop = "$outDeps/Desktop" $Configuration = 'Debug' if ($Release) { @@ -61,7 +65,9 @@ if ((Test-Path "$cmdletsSrc/bin") -or (Test-Path "$cmdletsSrc/obj")) { Write-Host -ForegroundColor Green 'Compiling module...' # Build Authentication.Core Push-Location $coreSrc -dotnet publish -c $Configuration --verbosity quiet /nologo +dotnet publish -c $Configuration -f $netStandard --verbosity quiet /nologo +dotnet publish -c $Configuration -f $netCoreApp --verbosity quiet /nologo +dotnet publish -c $Configuration -f $netFx --verbosity quiet /nologo Pop-Location # Build Authentication @@ -76,26 +82,41 @@ if ($LastExitCode -ne 0) { # Ensure out directory exists and is clean Remove-Item -Path $outDir -Recurse -ErrorAction Ignore New-Item -Path $outDir -ItemType Directory +# New-Item -Path $outStartupScripts -ItemType Directory New-Item -Path $outDeps -ItemType Directory +New-Item -Path $outCore -ItemType Directory +New-Item -Path $outDesktop -ItemType Directory # Copy manifest. Copy-Item -Path "$cmdletsSrc/$ModulePrefix.$ModuleName.format.ps1xml" -Destination $outDir Copy-Item -Path "$cmdletsSrc/$ModulePrefix.$ModuleName.psm1" -Destination $outDir Copy-Item -Path "$cmdletsSrc/$ModulePrefix.$ModuleName.psd1" -Destination $outDir +Copy-Item -Path "$cmdletsSrc/StartupScripts" -Recurse -Destination $outDir # Core assemblies to include with cmdlets (Let PowerShell load them). $CoreAssemblies = @('Microsoft.Graph.Authentication.Core', 'Microsoft.Graph.Core') +$MultiFrameworkDependencies = @('Microsoft.Identity.Client', 'System.Security.Cryptography.ProtectedData') # Copy each core asset and remember it. $Deps = [System.Collections.Generic.HashSet[string]]::new() Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netStandard/publish/" | - Where-Object { $_.Extension -in $copyExtensions } | - Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | - ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outDeps } +Where-Object { $_.Extension -in $copyExtensions } | +Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | +ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outDeps } + +Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netCoreApp/publish/" | +Where-Object { $MultiFrameworkDependencies.Contains($_.BaseName) } | +Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | +ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outCore } + +Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netFx/publish/" | +Where-Object { $MultiFrameworkDependencies.Contains($_.BaseName) } | +Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | +ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outDesktop } # Now copy each Cmdlets asset, not taking any found in Engine. Get-ChildItem -Path "$cmdletsSrc/bin/$Configuration/$netStandard/publish/" | - Where-Object { -not $Deps.Contains($_.Name) -and $_.Extension -in $copyExtensions } | - ForEach-Object { Copy-Item -Path $_.FullName -Destination $outDir } +Where-Object { -not $Deps.Contains($_.Name) -and $_.Extension -in $copyExtensions } | +ForEach-Object { Copy-Item -Path $_.FullName -Destination $outDir } Write-Host -ForegroundColor Green '-------------Done-------------' \ No newline at end of file From 6554febe0fd814a1408e8a6ac5a78f01ac1d0bd2 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Fri, 12 Mar 2021 13:06:42 -0800 Subject: [PATCH 07/10] Clean up dependency assembly resolver. --- .../Microsoft.Graph.Authentication.Core.csproj | 1 + .../Microsoft.Graph.Authentication.csproj | 3 +-- .../Microsoft.Graph.Authentication.nuspec | 10 +++++++--- .../Microsoft.Graph.Authentication.psd1 | 2 +- .../Utilities/DependencyAssemblyResolver.cs | 10 +--------- src/Authentication/Authentication/build-module.ps1 | 14 +++++--------- .../Authentication/test/Connect-MgGraph.Tests.ps1 | 2 +- tools/CSProjHelper.ps1 | 7 ++++++- 8 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj index 97caafea8f7..9832da8843a 100644 --- a/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj +++ b/src/Authentication/Authentication.Core/Microsoft.Graph.Authentication.Core.csproj @@ -3,6 +3,7 @@ netstandard2.0;netcoreapp2.1;net461 Microsoft.Graph.PowerShell.Authentication.Core + 1.4.2 diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj b/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj index f2a897e6557..17acca82e6a 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.csproj @@ -1,6 +1,6 @@ - 1.4.0 + 1.4.2 7.1 netstandard2.0 Library @@ -16,7 +16,6 @@ - diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec b/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec index 51bd046dba8..01fe73c6929 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.nuspec @@ -1,7 +1,7 @@ - 1.3.1 + 1.4.2 Microsoft.Graph.Authentication Microsoft Graph PowerShell authentication module Microsoft @@ -23,13 +23,17 @@ + - - + + + + + \ No newline at end of file diff --git a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 index 6f306c3eabb..b080bc96751 100644 --- a/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 +++ b/src/Authentication/Authentication/Microsoft.Graph.Authentication.psd1 @@ -3,7 +3,7 @@ # # Generated by: Microsoft # -# Generated on: 3/2/2021 +# Generated on: 3/12/2021 # @{ diff --git a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs index 16f4719f887..239cdd300bb 100644 --- a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs +++ b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs @@ -44,15 +44,12 @@ public static void Initialize(bool isDesktopEdition = false) { FrameworkDependenciesDirPath = "Core"; } - Console.WriteLine($"Initialize"); // Set up our event handler when the module is loaded. AppDomain.CurrentDomain.AssemblyResolve += HandleResolveEvent; } internal static void Reset() - { - Console.WriteLine($"Resting"); - // Remove our event hander when the module is unloaded. + { // Remove our event hander when the module is unloaded. AppDomain.CurrentDomain.AssemblyResolve -= HandleResolveEvent; } @@ -60,11 +57,7 @@ private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) { try { - string ver = Assembly.GetEntryAssembly()?.GetCustomAttribute()?.FrameworkName; - Console.WriteLine($".NET Version: {ver}"); - AssemblyName assemblymName = new AssemblyName(args.Name); - Console.WriteLine($"Loading: {assemblymName.Name}"); // We try to resolve our dependencies on our own. if (Dependencies.TryGetValue(assemblymName.Name, out Version requiredVersion) && requiredVersion >= assemblymName.Version @@ -79,7 +72,6 @@ private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) { requiredAssemblyPath = Path.Combine(DependenciesDirPath, $"{assemblymName.Name}.dll"); } - Console.WriteLine($"Loading for engine assembly: {assemblymName.Name}"); return Assembly.LoadFile(requiredAssemblyPath); } } diff --git a/src/Authentication/Authentication/build-module.ps1 b/src/Authentication/Authentication/build-module.ps1 index d2dfa0e19f2..2a0b2944807 100644 --- a/src/Authentication/Authentication/build-module.ps1 +++ b/src/Authentication/Authentication/build-module.ps1 @@ -63,14 +63,14 @@ if ((Test-Path "$cmdletsSrc/bin") -or (Test-Path "$cmdletsSrc/obj")) { } Write-Host -ForegroundColor Green 'Compiling module...' -# Build Authentication.Core +# Build authentication.core for each framework. Push-Location $coreSrc dotnet publish -c $Configuration -f $netStandard --verbosity quiet /nologo dotnet publish -c $Configuration -f $netCoreApp --verbosity quiet /nologo dotnet publish -c $Configuration -f $netFx --verbosity quiet /nologo Pop-Location -# Build Authentication +# Build authentication. Push-Location $cmdletsSrc dotnet publish -c $Configuration --verbosity quiet /nologo Pop-Location @@ -79,10 +79,9 @@ if ($LastExitCode -ne 0) { Write-Error 'Compilation failed.' } -# Ensure out directory exists and is clean +# Ensure out directory exists and is clean. Remove-Item -Path $outDir -Recurse -ErrorAction Ignore New-Item -Path $outDir -ItemType Directory -# New-Item -Path $outStartupScripts -ItemType Directory New-Item -Path $outDeps -ItemType Directory New-Item -Path $outCore -ItemType Directory New-Item -Path $outDesktop -ItemType Directory @@ -95,9 +94,8 @@ Copy-Item -Path "$cmdletsSrc/StartupScripts" -Recurse -Destination $outDir # Core assemblies to include with cmdlets (Let PowerShell load them). $CoreAssemblies = @('Microsoft.Graph.Authentication.Core', 'Microsoft.Graph.Core') -$MultiFrameworkDependencies = @('Microsoft.Identity.Client', 'System.Security.Cryptography.ProtectedData') -# Copy each core asset and remember it. +# Copy each authentication.core asset to out directory and remember it. $Deps = [System.Collections.Generic.HashSet[string]]::new() Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netStandard/publish/" | Where-Object { $_.Extension -in $copyExtensions } | @@ -105,16 +103,14 @@ Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outDeps } Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netCoreApp/publish/" | -Where-Object { $MultiFrameworkDependencies.Contains($_.BaseName) } | Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outCore } Get-ChildItem -Path "$coreSrc/bin/$Configuration/$netFx/publish/" | -Where-Object { $MultiFrameworkDependencies.Contains($_.BaseName) } | Where-Object { -not $CoreAssemblies.Contains($_.BaseName) } | ForEach-Object { [void]$Deps.Add($_.Name); Copy-Item -Path $_.FullName -Destination $outDesktop } -# Now copy each Cmdlets asset, not taking any found in Engine. +# Now copy each authentication asset, not taking any found in authentication.core. Get-ChildItem -Path "$cmdletsSrc/bin/$Configuration/$netStandard/publish/" | Where-Object { -not $Deps.Contains($_.Name) -and $_.Extension -in $copyExtensions } | ForEach-Object { Copy-Item -Path $_.FullName -Destination $outDir } diff --git a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 index f95685d3534..76845efcd07 100644 --- a/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 +++ b/src/Authentication/Authentication/test/Connect-MgGraph.Tests.ps1 @@ -26,7 +26,7 @@ Describe 'Connect-MgGraph In App Mode' { } Describe 'Connect-MgGraph Depencency Resolution' { BeforeAll { - Install-Module Az.Accounts -Repository PSGallery -Scope CurrentUser -Force + Install-Module Az.Accounts -Repository PSGallery -Force } It 'ShouldLoadMgModuleSideBySideWithAzModule.' { diff --git a/tools/CSProjHelper.ps1 b/tools/CSProjHelper.ps1 index aada431fba1..65150f373a8 100644 --- a/tools/CSProjHelper.ps1 +++ b/tools/CSProjHelper.ps1 @@ -14,7 +14,12 @@ function Set-CSProjValues( # Set delay sign to true. $ModuleProjDoc = New-Object System.Xml.XmlDocument $ModuleProjDoc.Load($ModuleCsProj) - $ModuleProjElement = [System.Xml.XmlElement] $ModuleProjDoc.DocumentElement.PropertyGroup + if ($ModuleProjDoc.DocumentElement.PropertyGroup.Count -gt 1) { + $ModuleProjElement = [System.Xml.XmlElement] $ModuleProjDoc.DocumentElement.PropertyGroup[0] + } else { + $ModuleProjElement = [System.Xml.XmlElement] $ModuleProjDoc.DocumentElement.PropertyGroup + } + if (![string]::IsNullOrWhiteSpace($AssemblyOriginatorKeyFile)) { Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "AssemblyOriginatorKeyFile" -ElementValue (Join-Path $PSScriptRoot $AssemblyOriginatorKeyFile) Set-ElementValue -XmlDocument $ModuleProjDoc -MetadataElement $ModuleProjElement -ElementName "DelaySign" -ElementValue "true" From 39e6da5522c8e42dce6df7076eeabbb06c016982 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Fri, 12 Mar 2021 15:52:26 -0800 Subject: [PATCH 08/10] Sign auth.core assembly. --- .../generate-auth-module-template.yml | 8 +- .azure-pipelines/generate-auth-module.yml | 164 ----------- .azure-pipelines/generate-beta-modules.yml | 267 ------------------ .../generate-beta-rollup-module.yml | 132 --------- .../generate-modules-template.yml | 8 +- .../generate-service-modules.yml | 8 +- .azure-pipelines/validate-pr-auth-module.yml | 96 ------- .azure-pipelines/validate-pr-beta-modules.yml | 62 ---- 8 files changed, 6 insertions(+), 739 deletions(-) delete mode 100644 .azure-pipelines/generate-auth-module.yml delete mode 100644 .azure-pipelines/generate-beta-modules.yml delete mode 100644 .azure-pipelines/generate-beta-rollup-module.yml delete mode 100644 .azure-pipelines/validate-pr-auth-module.yml delete mode 100644 .azure-pipelines/validate-pr-beta-modules.yml diff --git a/.azure-pipelines/generate-auth-module-template.yml b/.azure-pipelines/generate-auth-module-template.yml index ffa015ac270..0600456da0a 100644 --- a/.azure-pipelines/generate-auth-module-template.yml +++ b/.azure-pipelines/generate-auth-module-template.yml @@ -75,9 +75,7 @@ jobs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) # Recursively finds files matching these patterns: - Pattern: | - **/Microsoft.Graph.Authentication.dll - **/Microsoft.Graph.Authentication.Core.dll + Pattern: 'Microsoft.Graph.Authentication.dll, Microsoft.Graph.Authentication.Core.dll' signConfigType: inlineSignParams inlineOperation: | [ @@ -105,9 +103,7 @@ jobs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) # Recursively finds files matching these patterns: - Pattern: | - **/Microsoft.Graph.Authentication.dll - **/Microsoft.Graph.Authentication.Core.dll + Pattern: 'Microsoft.Graph.Authentication.dll, Microsoft.Graph.Authentication.Core.dll, InitializeAssemblyResolver.ps1' signConfigType: inlineSignParams inlineOperation: | [ diff --git a/.azure-pipelines/generate-auth-module.yml b/.azure-pipelines/generate-auth-module.yml deleted file mode 100644 index 3d74a18f287..00000000000 --- a/.azure-pipelines/generate-auth-module.yml +++ /dev/null @@ -1,164 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -# Generates a release build artifact (nuget) from HEAD of master for auth module. -name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) -trigger: - branches: - include: - - master - paths: - include: - - src/Authentication/* -pr: none -variables: - MODULE_NAME: 'Authentication' - MODULE_PATH: 'src\Authentication\Authentication\' - MODULE_DLL_PATTERN: 'Microsoft.Graph.Authentication.dll' - -jobs: -- job: MSGraphPSSDKGeneration - displayName: MS Graph PS SDK Auth Generation - timeoutInMinutes: 300 - pool: - vmImage: 'windows-latest' - - steps: - - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 - displayName: 'Run CredScan' - inputs: - debugMode: false - - - task: NuGetToolInstaller@1 - displayName: 'Install Nuget' - - - task: PowerShell@2 - displayName: 'Generate and Build Auth Module' - inputs: - filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateAuthenticationModule.ps1' - arguments: '-ArtifactsLocation $(Build.ArtifactStagingDirectory) -Build -EnableSigning' - pwsh: true - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP DLL Strong Name (Graph Auth Module)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' - FolderPath: $(MODULE_PATH) - Pattern: $(MODULE_DLL_PATTERN) - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-233863-SN", - "operationSetCode": "StrongNameSign", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-233863-SN", - "operationSetCode": "StrongNameVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP DLL CodeSigning (Graph Auth Module)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' - FolderPath: $(MODULE_PATH) - Pattern: $(MODULE_DLL_PATTERN) - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: PowerShell@2 - displayName: 'Pack Auth Module' - inputs: - targetType: 'inline' - script: | - & $(System.DefaultWorkingDirectory)/tools/PackModule.ps1 -Module $(MODULE_NAME) -ArtifactsLocation $(Build.ArtifactStagingDirectory) - pwsh: true - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP NuGet CodeSigning' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' - FolderPath: '$(Build.ArtifactStagingDirectory)\$(MODULE_NAME)' - Pattern: 'Microsoft.Graph.$(MODULE_NAME)*.nupkg' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: PublishBuildArtifacts@1 - displayName: Publish Artifact Microsoft.Graph.Authentication.nupkg' - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/$(MODULE_NAME)' - ArtifactName: 'drop' - publishLocation: 'Container' - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true diff --git a/.azure-pipelines/generate-beta-modules.yml b/.azure-pipelines/generate-beta-modules.yml deleted file mode 100644 index f41459c1093..00000000000 --- a/.azure-pipelines/generate-beta-modules.yml +++ /dev/null @@ -1,267 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -# Generates a release build artifact (nuget) from HEAD of master for beta Graph workload modules. -name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) -trigger: - branches: - include: - - master - paths: - include: - - src/* - - config/ModulesMapping.jsonc - exclude: - - src/Authentication/* -pr: none -variables: - MODULE_PREFIX: 'Microsoft.Graph' - WORKLOAD_MODULE_PATH: 'src\' - AUTH_MODULE_PATH: 'src\Authentication\Authentication\bin\' - AUTH_MODULE_DLL_PATTERN: 'Microsoft.Graph.Authentication.dll' - -jobs: -- job: MSGraphPSSDKGeneration - displayName: MS Graph PS SDK Beta Generation - timeoutInMinutes: 800 - pool: - name: Microsoft Graph - demands: 'Agent.Name -equals PS-Build-Agent' - - steps: - - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 - displayName: 'Run CredScan' - inputs: - debugMode: false - - # Install Node - - task: NodeTool@0 - displayName: Node install - inputs: - versionSpec: '13.14.0' - - - task: Npm@1 - displayName: 'Install AutoRest' - inputs: - command: 'custom' - customCommand: 'install -g autorest' - - - task: NuGetToolInstaller@1 - displayName: 'Install Nuget' - - - task: PowerShell@2 - displayName: 'Build Auth Modules' - inputs: - filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateAuthenticationModule.ps1' - arguments: '-ArtifactsLocation $(Build.ArtifactStagingDirectory) -Build -BuildWhenEqual -EnableSigning' - pwsh: true - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP DLL Strong Name (Graph Auth Module)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: $(AUTH_MODULE_PATH) - Pattern: '$(AUTH_MODULE_DLL_PATTERN)' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-233863-SN", - "operationSetCode": "StrongNameSign", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-233863-SN", - "operationSetCode": "StrongNameVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP DLL CodeSigning (Graph Auth Module)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: $(AUTH_MODULE_PATH) - Pattern: $(AUTH_MODULE_DLL_PATTERN) - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: PowerShell@2 - displayName: 'Generate and Build Graph Resource Modules' - inputs: - filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateModules.ps1' - arguments: '-ArtifactsLocation $(Build.ArtifactStagingDirectory)\ -Build -Test -EnableSigning' - pwsh: true - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP DLL Strong Name (Graph Resource Modules)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: $(WORKLOAD_MODULE_PATH) - Pattern: '$(MODULE_PREFIX).*.private.dll' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-233863-SN", - "operationSetCode": "StrongNameSign", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-233863-SN", - "operationSetCode": "StrongNameVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP DLL CodeSigning (Graph Resource Module)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: $(WORKLOAD_MODULE_PATH) - Pattern: '$(MODULE_PREFIX).*.private.dll, $(MODULE_PREFIX).*.psm1, $(MODULE_PREFIX).*.format.ps1xml, ProxyCmdletDefinitions.ps1, load-dependency.ps1' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 160 - - - task: PowerShell@2 - displayName: 'Pack Modules' - inputs: - targetType: 'inline' - script: | - $ModuleMappingConfigPath = "$(System.DefaultWorkingDirectory)/config/ModulesMapping.jsonc" - [HashTable] $ModuleMapping = Get-Content $ModuleMappingConfigPath | ConvertFrom-Json -AsHashTable - $ModuleMapping.Keys | ForEach-Object { - $ModuleName = $_ - $ModuleProjectDir = "$(System.DefaultWorkingDirectory)/src/$ModuleName/$ModuleName" - & $(System.DefaultWorkingDirectory)/tools/PackModule.ps1 -Module $ModuleName -ArtifactsLocation $(Build.ArtifactStagingDirectory)\ - } - pwsh: true - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP NuGet CodeSigning' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: '$(Build.ArtifactStagingDirectory)\' - Pattern: '*.nupkg' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: PublishBuildArtifacts@1 - displayName: Publish Artifact Beta Modules - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/' - ArtifactName: 'drop' - publishLocation: 'Container' - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true \ No newline at end of file diff --git a/.azure-pipelines/generate-beta-rollup-module.yml b/.azure-pipelines/generate-beta-rollup-module.yml deleted file mode 100644 index a2179ae12a8..00000000000 --- a/.azure-pipelines/generate-beta-rollup-module.yml +++ /dev/null @@ -1,132 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -# Generates a release build artifact (nuget) for beta roll-up module. -name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) -trigger: none -pr: none -variables: - MODULE_PREFIX: 'Microsoft.Graph' - MODULE_NAME: 'Graph' - MODULE_PATH: 'src/Graph/Graph' - -jobs: -- job: MSGraphPSSDKGeneration - displayName: MS Graph PS SDK Roll-Up Generation - timeoutInMinutes: 300 - pool: - vmImage: 'windows-latest' - - steps: - - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 - displayName: 'Run CredScan' - inputs: - debugMode: false - - - task: NuGetToolInstaller@1 - displayName: 'Install Nuget' - - - task: PowerShell@2 - displayName: 'Generate and Build Roll-Up Module' - inputs: - filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateRollUpModule.ps1' - pwsh: true - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP CodeSigning (Graph Roll-Up Module)' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: $(MODULE_PATH) - Pattern: '$(MODULE_PREFIX).psm1, $(MODULE_PREFIX).*.format.ps1xml, *.ps1' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolSign", - "parameters": [ - { - "parameterName": "OpusName", - "parameterValue": "Microsoft" - }, - { - "parameterName": "OpusInfo", - "parameterValue": "http://www.microsoft.com" - }, - { - "parameterName": "FileDigest", - "parameterValue": "/fd \"SHA256\"" - }, - { - "parameterName": "PageHash", - "parameterValue": "/NPH" - }, - { - "parameterName": "TimeStamp", - "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" - } - ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-230012", - "operationSetCode": "SigntoolVerify", - "parameters": [], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: NuGetCommand@2 - displayName: 'Pack Roll-Up Module' - inputs: - command: 'pack' - Configuration: Release - packagesToPack: '$(System.DefaultWorkingDirectory)/$(MODULE_PATH)/$(MODULE_PREFIX).nuspec' - packDestination: '$(Build.ArtifactStagingDirectory)/' - versioningScheme: 'off' - - - task: SFP.build-tasks.custom-build-task-1.EsrpCodeSigning@1 - displayName: 'ESRP NuGet CodeSigning' - inputs: - ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet' - FolderPath: '$(Build.ArtifactStagingDirectory)/' - Pattern: 'Microsoft.Graph*.nupkg' - signConfigType: inlineSignParams - inlineOperation: | - [ - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetSign", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - }, - { - "keyCode": "CP-401405", - "operationSetCode": "NuGetVerify", - "parameters": [ ], - "toolName": "sign", - "toolVersion": "1.0" - } - ] - SessionTimeout: 20 - - - task: PublishBuildArtifacts@1 - displayName: Publish Artifact Microsoft.Graph.nupkg' - inputs: - PathtoPublish: '$(Build.ArtifactStagingDirectory)/' - ArtifactName: 'drop' - publishLocation: 'Container' - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true \ No newline at end of file diff --git a/.azure-pipelines/generate-modules-template.yml b/.azure-pipelines/generate-modules-template.yml index eb0d12c939c..40a844f8a05 100644 --- a/.azure-pipelines/generate-modules-template.yml +++ b/.azure-pipelines/generate-modules-template.yml @@ -62,9 +62,7 @@ jobs: inputs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) - Pattern: | - **/Microsoft.Graph.Authentication.dll - **/Microsoft.Graph.Authentication.Core.dll + Pattern: 'Microsoft.Graph.Authentication.dll, Microsoft.Graph.Authentication.Core.dll' signConfigType: inlineSignParams inlineOperation: | [ @@ -91,9 +89,7 @@ jobs: inputs: ConnectedServiceName: 'microsoftgraph ESRP CodeSign DLL and NuGet (AKV)' FolderPath: $(AUTH_MODULE_PATH) - Pattern: | - **/Microsoft.Graph.Authentication.dll - **/Microsoft.Graph.Authentication.Core.dll + Pattern: 'Microsoft.Graph.Authentication.dll, Microsoft.Graph.Authentication.Core.dll' signConfigType: inlineSignParams inlineOperation: | [ diff --git a/.azure-pipelines/generation-templates/generate-service-modules.yml b/.azure-pipelines/generation-templates/generate-service-modules.yml index af820e4f5d9..dd2c922ff8a 100644 --- a/.azure-pipelines/generation-templates/generate-service-modules.yml +++ b/.azure-pipelines/generation-templates/generate-service-modules.yml @@ -61,9 +61,7 @@ jobs: inputs: ConnectedServiceName: "microsoftgraph ESRP CodeSign DLL and NuGet (AKV)" FolderPath: $(AuthModulePath) - Pattern: | - **/Microsoft.Graph.Authentication.dll - **/Microsoft.Graph.Authentication.Core.dll + Pattern: 'Microsoft.Graph.Authentication.dll, Microsoft.Graph.Authentication.Core.dll' signConfigType: inlineSignParams inlineOperation: | [ @@ -90,9 +88,7 @@ jobs: inputs: ConnectedServiceName: "microsoftgraph ESRP CodeSign DLL and NuGet (AKV)" FolderPath: $(AuthModulePath) - Pattern: | - **/Microsoft.Graph.Authentication.dll - **/Microsoft.Graph.Authentication.Core.dll + Pattern: 'Microsoft.Graph.Authentication.dll, Microsoft.Graph.Authentication.Core.dll' signConfigType: inlineSignParams inlineOperation: | [ diff --git a/.azure-pipelines/validate-pr-auth-module.yml b/.azure-pipelines/validate-pr-auth-module.yml deleted file mode 100644 index 1dd1b386db7..00000000000 --- a/.azure-pipelines/validate-pr-auth-module.yml +++ /dev/null @@ -1,96 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -# Validate pull requests to master and dev branches for auth module. -name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) -pr: - branches: - include: - - dev - - master - - milestone/* - paths: - include: - - src/Authentication/* -trigger: none - -jobs: -- job: MSGraphPSSDKValidation_Windows - displayName: MS Graph PS SDK Auth Validation - Windows - timeoutInMinutes: 300 - pool: - vmImage: 'windows-latest' - - steps: - - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 - displayName: 'Run CredScan' - inputs: - debugMode: false - - - task: PowerShell@2 - displayName: 'Generate and Build Auth Module' - inputs: - filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateAuthenticationModule.ps1' - arguments: '-RepositoryApiKey $(Api_Key) -ArtifactsLocation $(Build.ArtifactStagingDirectory) -Build' - pwsh: true - - - task: DotNetCoreCLI@2 - displayName: 'Run Enabled Tests' - inputs: - command: 'test' - projects: '$(System.DefaultWorkingDirectory)/src/Authentication/Authentication.Test/*.csproj' - testRunTitle: 'Run Enabled Tests' - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true - -- job: MSGraphPSSDKValidation_Linux - displayName: MS Graph PS SDK Auth Validation - Linux - pool: - vmImage: 'ubuntu-latest' - steps: - - task: DotNetCoreCLI@2 - displayName: 'Run Enabled Tests' - inputs: - command: 'test' - projects: '$(System.DefaultWorkingDirectory)/src/Authentication/Authentication.Test/*.csproj' - testRunTitle: 'Run Enabled Tests' - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true - -- job: MSGraphPSSDKValidation_MacOS - displayName: MS Graph PS SDK Auth Validation - MacOS - pool: - vmImage: 'macOS-latest' - steps: - - task: DotNetCoreCLI@2 - displayName: 'Run Enabled Tests' - inputs: - command: 'test' - projects: '$(System.DefaultWorkingDirectory)/src/Authentication/Authentication.Test/*.csproj' - testRunTitle: 'Run Enabled Tests' - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true \ No newline at end of file diff --git a/.azure-pipelines/validate-pr-beta-modules.yml b/.azure-pipelines/validate-pr-beta-modules.yml deleted file mode 100644 index c89258b16e5..00000000000 --- a/.azure-pipelines/validate-pr-beta-modules.yml +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright (c) Microsoft Corporation. All rights reserved. -# Licensed under the MIT License. - -# Validate pull requests to master and dev branches for Graph workload modules. -name: $(BuildDefinitionName)_$(SourceBranchName)_$(Date:yyyyMMdd)$(Rev:.r) -pr: - branches: - include: - - master - - dev - - milestone/* - paths: - include: - - src/Beta/* - - config/ModulesMapping.jsonc -trigger: none - -variables: - GRAPH_VERSION: 'beta' - -jobs: -- job: MSGraphPSSDKValidation - displayName: MS Graph PS SDK Beta Validation - timeoutInMinutes: 600 - pool: - name: Microsoft Graph - demands: 'Agent.Name -equals PS-Build-Agent' - - steps: - - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 - displayName: 'Run CredScan' - inputs: - debugMode: false - - # Install Node - - task: NodeTool@0 - displayName: Node install - inputs: - versionSpec: '13.14.0' - - - task: Npm@1 - displayName: 'Install AutoRest' - inputs: - command: 'custom' - customCommand: 'install -g autorest' - - - task: PowerShell@2 - displayName: 'Generate and Build Graph Resource Modules' - inputs: - filePath: '$(System.DefaultWorkingDirectory)/tools/GenerateModules.ps1' - arguments: '-RepositoryApiKey $(Api_Key) -Build -Test' - pwsh: true - - - task: YodLabs.O365PostMessage.O365PostMessageBuild.O365PostMessageBuild@0 - displayName: 'Graph Client Tooling pipeline fail notification' - inputs: - addressType: serviceEndpoint - serviceEndpointName: 'microsoftgraph pipeline status' - title: '$(Build.DefinitionName) failure notification' - text: 'This pipeline has failed. View the build details for further information. This is a blocking failure. ' - condition: and(failed(), ne(variables['Build.Reason'], 'Manual')) - enabled: true \ No newline at end of file From 0ddade866d4c6ac7221046e540c53c40464e38b1 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Fri, 12 Mar 2021 17:06:40 -0800 Subject: [PATCH 09/10] Add code comment. --- .../Authentication.Core/Authenticator.cs | 47 ++---- .../Authentication.Core/ErrorConstants.cs | 2 +- .../Utilities/AuthenticationHelpers.cs | 147 ++++++++++-------- .../Utilities/JwtHelpers.cs | 37 +++++ .../Authentication/ErrorConstants.cs | 6 +- .../Utilities/DependencyAssemblyResolver.cs | 19 ++- 6 files changed, 158 insertions(+), 100 deletions(-) diff --git a/src/Authentication/Authentication.Core/Authenticator.cs b/src/Authentication/Authentication.Core/Authenticator.cs index bb475d5a5cb..fe23f3a1a1c 100644 --- a/src/Authentication/Authentication.Core/Authenticator.cs +++ b/src/Authentication/Authentication.Core/Authenticator.cs @@ -8,7 +8,6 @@ namespace Microsoft.Graph.Authentication.Core using Microsoft.Graph.PowerShell.Authentication; using Microsoft.Graph.PowerShell.Authentication.Core; using Microsoft.Graph.PowerShell.Authentication.Helpers; - using Microsoft.Graph.PowerShell.Authentication.Models; using Microsoft.Identity.Client; using System; using System.Collections.Generic; @@ -18,8 +17,18 @@ namespace Microsoft.Graph.Authentication.Core using System.Threading; using System.Threading.Tasks; + /// + /// Authenticator class for handling sign-ins and sign-outs. + /// public static class Authenticator { + /// + /// Authenticates the client using the provided . + /// + /// The to authenticate. + /// Whether or not to force refresh a token if one exists. + /// The cancellation token. + /// public static async Task AuthenticateAsync(IAuthContext authContext, bool forceRefresh, CancellationToken cancellationToken) { try @@ -67,7 +76,7 @@ public static async Task AuthenticateAsync(IAuthContext authContex account = accounts.FirstOrDefault(); } - DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); + JwtHelpers.DecodeJWT(httpRequestMessage.Headers.Authorization?.Parameter, account, ref authContext); return authContext; } catch (AuthenticationException authEx) @@ -91,39 +100,13 @@ public static async Task AuthenticateAsync(IAuthContext authContex } } + /// + /// Signs out of the provided . + /// + /// The to sign-out from. public static void LogOut(IAuthContext authContext) { AuthenticationHelpers.Logout(authContext); } - - private static void DecodeJWT(string token, IAccount account, ref IAuthContext authContext) - { - JwtPayload jwtPayload = JwtHelpers.DecodeToObject(token); - if (authContext.AuthType == AuthenticationType.UserProvidedAccessToken) - { - if (jwtPayload == null) - { - throw new Exception(string.Format( - CultureInfo.CurrentCulture, - ErrorConstants.Message.InvalidUserProvidedToken, - "AccessToken")); - } - - if (jwtPayload.Exp <= JwtHelpers.ConvertToUnixTimestamp(DateTime.UtcNow + TimeSpan.FromMinutes(Constants.TokenExpirationBufferInMinutes))) - { - throw new Exception(string.Format( - CultureInfo.CurrentCulture, - ErrorConstants.Message.ExpiredUserProvidedToken, - "AccessToken")); - } - } - - authContext.ClientId = jwtPayload?.Appid ?? authContext.ClientId; - authContext.Scopes = jwtPayload?.Scp?.Split(' ') ?? jwtPayload?.Roles; - authContext.TenantId = jwtPayload?.Tid ?? account?.HomeAccountId?.TenantId; - authContext.AppName = jwtPayload?.AppDisplayname; - authContext.Account = jwtPayload?.Upn ?? account?.Username; - } - } } diff --git a/src/Authentication/Authentication.Core/ErrorConstants.cs b/src/Authentication/Authentication.Core/ErrorConstants.cs index acd1add6348..3ee456579e6 100644 --- a/src/Authentication/Authentication.Core/ErrorConstants.cs +++ b/src/Authentication/Authentication.Core/ErrorConstants.cs @@ -19,8 +19,8 @@ internal static class Codes public static class Message { - internal const string InvalidJWT = "Invalid JWT access token."; public const string MissingAuthContext = "Authentication needed, call Connect-MgGraph."; + internal const string InvalidJWT = "Invalid JWT access token."; internal const string NullOrEmptyParameter = "Parameter '{0}' cannot be null or empty."; internal const string MacKeyChainFailed = "{0} failed with result code {1}."; internal const string DeviceCodeTimeout = "Device code terminal timed-out after {0} seconds. Please try again."; diff --git a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs index 1359c454b67..0d3eb662b18 100644 --- a/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/AuthenticationHelpers.cs @@ -18,10 +18,42 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers using AuthenticationException = System.Security.Authentication.AuthenticationException; + /// + /// Helper class for authentication. + /// public static class AuthenticationHelpers { static ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); + /// + /// Signs out of the current session using the provided . + /// + /// The to sign-out from. + internal static void Logout(IAuthContext authContext) + { + try + { + _cacheLock.EnterWriteLock(); + if (authContext.AuthType == AuthenticationType.UserProvidedAccessToken) + { + GraphSession.Instance.UserProvidedToken = null; + } + else + { + TokenCacheStorage.DeleteToken(authContext); + } + } + finally + { + _cacheLock.ExitWriteLock(); + } + } + + /// + /// Gets an using the provide . + /// + /// The to get an auth provider for. + /// A based on provided . public static IAuthenticationProvider GetAuthProvider(IAuthContext authContext) { if (authContext is null) @@ -75,70 +107,12 @@ public static IAuthenticationProvider GetAuthProvider(IAuthContext authContext) } return authProvider; } + /// - /// Gets a certificate based on the current context. - /// Priority is Name, ThumbPrint, then In-Memory Cert + /// Configures a token cache using the provide . /// - /// Current context - /// A based on provided context - private static X509Certificate2 GetCertificate(IAuthContext context) - { - X509Certificate2 certificate; - if (!string.IsNullOrWhiteSpace(context.CertificateName)) - { - certificate = GetCertificateByName(context.CertificateName); - } - else if (!string.IsNullOrWhiteSpace(context.CertificateThumbprint)) - { - certificate = GetCertificateByThumbprint(context.CertificateThumbprint); - } - else - { - certificate = context.Certificate; - } - - if (certificate == null) - { - throw new ArgumentNullException(nameof(certificate), $"Certificate with the Specified ThumbPrint {context.CertificateThumbprint}, Name {context.CertificateName} or In-Memory could not be found"); - } - - return certificate; - } - - private static string GetAuthorityUrl(IAuthContext authContext) - { - string audience = authContext.TenantId ?? Constants.DefaulAdTenant; - string defaultInstance = Constants.DefaultAzureADEndpoint; - string authorityUrl = $"{defaultInstance}/{audience}"; - - if (GraphSession.Instance.Environment != null) - { - authorityUrl = $"{GraphSession.Instance.Environment.AzureADEndpoint}/{audience}"; - } - - return authorityUrl; - } - - internal static void Logout(IAuthContext authConfig) - { - try - { - _cacheLock.EnterWriteLock(); - if (authConfig.AuthType == AuthenticationType.UserProvidedAccessToken) - { - GraphSession.Instance.UserProvidedToken = null; - } - else - { - TokenCacheStorage.DeleteToken(authConfig); - } - } - finally - { - _cacheLock.ExitWriteLock(); - } - } - + /// MSAL's token cache to configure. + /// The to get configure an token cache for. private static void ConfigureTokenCache(ITokenCache tokenCache, IAuthContext authContext) { tokenCache.SetBeforeAccess((TokenCacheNotificationArgs args) => @@ -171,6 +145,55 @@ private static void ConfigureTokenCache(ITokenCache tokenCache, IAuthContext aut }); } + /// + /// Gets an authority URL from the provided . + /// + /// The to get an authority URL for. + /// + private static string GetAuthorityUrl(IAuthContext authContext) + { + string audience = authContext.TenantId ?? Constants.DefaulAdTenant; + string defaultInstance = Constants.DefaultAzureADEndpoint; + string authorityUrl = $"{defaultInstance}/{audience}"; + + if (GraphSession.Instance.Environment != null) + { + authorityUrl = $"{GraphSession.Instance.Environment.AzureADEndpoint}/{audience}"; + } + + return authorityUrl; + } + + /// + /// Gets a certificate based on the current context. + /// Priority is Name, ThumbPrint, then In-Memory Cert + /// + /// Current context + /// A based on provided context + private static X509Certificate2 GetCertificate(IAuthContext context) + { + X509Certificate2 certificate; + if (!string.IsNullOrWhiteSpace(context.CertificateName)) + { + certificate = GetCertificateByName(context.CertificateName); + } + else if (!string.IsNullOrWhiteSpace(context.CertificateThumbprint)) + { + certificate = GetCertificateByThumbprint(context.CertificateThumbprint); + } + else + { + certificate = context.Certificate; + } + + if (certificate == null) + { + throw new ArgumentNullException(nameof(certificate), $"Certificate with the Specified ThumbPrint {context.CertificateThumbprint}, Name {context.CertificateName} or In-Memory could not be found"); + } + + return certificate; + } + /// /// Gets unexpired certificate of the specified certificate thumbprint for the current user in My store. /// diff --git a/src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs b/src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs index 2a256f6bffc..c877d2785c5 100644 --- a/src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs +++ b/src/Authentication/Authentication.Core/Utilities/JwtHelpers.cs @@ -6,8 +6,10 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers { using Microsoft.Graph.Auth; using Microsoft.Graph.PowerShell.Authentication.Core; + using Microsoft.Identity.Client; using Newtonsoft.Json; using System; + using System.Globalization; using System.IdentityModel.Tokens.Jwt; /// @@ -15,6 +17,41 @@ namespace Microsoft.Graph.PowerShell.Authentication.Helpers /// internal static class JwtHelpers { + /// + /// Decodes a JWT token and store claims in the provided by ref. + /// + /// A JWT string. + /// MSAL's . + /// An to store JWT claims in. + internal static void DecodeJWT(string jwToken, IAccount account, ref IAuthContext authContext) + { + var jwtPayload = JwtHelpers.DecodeToObject(jwToken); + if (authContext.AuthType == AuthenticationType.UserProvidedAccessToken) + { + if (jwtPayload == null) + { + throw new Exception(string.Format( + CultureInfo.CurrentCulture, + ErrorConstants.Message.InvalidUserProvidedToken, + "AccessToken")); + } + + if (jwtPayload.Exp <= JwtHelpers.ConvertToUnixTimestamp(DateTime.UtcNow + TimeSpan.FromMinutes(Constants.TokenExpirationBufferInMinutes))) + { + throw new Exception(string.Format( + CultureInfo.CurrentCulture, + ErrorConstants.Message.ExpiredUserProvidedToken, + "AccessToken")); + } + } + + authContext.ClientId = jwtPayload?.Appid ?? authContext.ClientId; + authContext.Scopes = jwtPayload?.Scp?.Split(' ') ?? jwtPayload?.Roles; + authContext.TenantId = jwtPayload?.Tid ?? account?.HomeAccountId?.TenantId; + authContext.AppName = jwtPayload?.AppDisplayname; + authContext.Account = jwtPayload?.Upn ?? account?.Username; + } + /// /// Decodes a JWT token by extracting claims from the payload. /// diff --git a/src/Authentication/Authentication/ErrorConstants.cs b/src/Authentication/Authentication/ErrorConstants.cs index 533014ca92f..4f76a504412 100644 --- a/src/Authentication/Authentication/ErrorConstants.cs +++ b/src/Authentication/Authentication/ErrorConstants.cs @@ -29,10 +29,10 @@ internal static class Codes public const string InvokeGraphRequestCouldNotInferFileName = nameof(InvokeGraphRequestKeysWithDifferentCasingInJsonString); } - public static class Message + internal static class Message { - public const string CannotModifyBuiltInEnvironment = "Cannot {0} built-in environment {1}."; - public const string InvalidUrlParameter = "Parameter '{0}' has an invalid endpoint URL. Please use a valid URL with a network protocol i.e. [protocol]://[resource-name]."; + internal const string CannotModifyBuiltInEnvironment = "Cannot {0} built-in environment {1}."; + internal const string InvalidUrlParameter = "Parameter '{0}' has an invalid endpoint URL. Please use a valid URL with a network protocol i.e. [protocol]://[resource-name]."; internal const string InvalidEnvironment = "Unable to find environment with name '{0}'. Use Get-MgEnvironment to list available environments."; internal const string CannotAccessFile = "Could not {0} file at '{1}'. Please ensure you have access to this file and try again in a few minutes.."; internal const string InvalidCertificateThumbprint = "'{0}' must have a length of 40. Ensure you have the right certificate thumbprint then try again."; diff --git a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs index 239cdd300bb..82fe833d2dc 100644 --- a/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs +++ b/src/Authentication/Authentication/Utilities/DependencyAssemblyResolver.cs @@ -23,6 +23,9 @@ public static class DependencyAssemblyResolver { "Newtonsoft.Json", new Version("10.0.3.21018") }, }; + /// + /// Dependencies that need to be loaded per framework. + /// private static IList MultiFrameworkDependencies = new List { "Microsoft.Identity.Client", "System.Security.Cryptography.ProtectedData" @@ -32,8 +35,15 @@ public static class DependencyAssemblyResolver private static string DependenciesDirPath = Path.GetFullPath(Path.Combine( Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Dependencies")); + /// + /// Framework dependency path. /Desktop for PS 5.1, and /Core for PS 6+. + /// private static string FrameworkDependenciesDirPath; + /// + /// Initializes our custom assembly resolve event handler. This should be called on module import. + /// + /// public static void Initialize(bool isDesktopEdition = false) { if (isDesktopEdition) @@ -48,11 +58,17 @@ public static void Initialize(bool isDesktopEdition = false) AppDomain.CurrentDomain.AssemblyResolve += HandleResolveEvent; } + /// + /// Remove our custom assembly resolve event handler from the current app domain. + /// This should be called when our module is removed. + /// internal static void Reset() - { // Remove our event hander when the module is unloaded. + { + // Remove our event hander when the module is unloaded. AppDomain.CurrentDomain.AssemblyResolve -= HandleResolveEvent; } + private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) { try @@ -79,7 +95,6 @@ private static Assembly HandleResolveEvent(object sender, ResolveEventArgs args) { // If an error is encountered, we fall back to PowerShell's default dependency resolution. } - return null; } } From f4061e391cd5bdddcca582d43e7debbac1b0caa9 Mon Sep 17 00:00:00 2001 From: Peter Ombwa Date: Tue, 16 Mar 2021 10:26:07 -0700 Subject: [PATCH 10/10] Import local auth module when running tests. --- src/Authentication/Authentication/build-module.ps1 | 9 ++++----- .../test/v1.0-beta/New-MgInvitation.Tests.ps1 | 4 ++-- .../test/v1.0/New-MgInvitation.Tests.ps1 | 4 ++-- tools/TestModule.ps1 | 3 +++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/Authentication/Authentication/build-module.ps1 b/src/Authentication/Authentication/build-module.ps1 index 2a0b2944807..c6403a143b7 100644 --- a/src/Authentication/Authentication/build-module.ps1 +++ b/src/Authentication/Authentication/build-module.ps1 @@ -22,15 +22,15 @@ $outDeps = "$outDir/Dependencies" $outCore = "$outDeps/Core" $outDesktop = "$outDeps/Desktop" +if ($PSEdition -ne 'Core') { + Write-Error 'This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell.' +} + $Configuration = 'Debug' if ($Release) { $Configuration = 'Release' } -if ($PSEdition -ne 'Core') { - Write-Error 'This script requires PowerShell Core to execute. [Note] Generated cmdlets will work in both PowerShell Core or Windows PowerShell.' -} - if (-not $Isolated) { Write-Host -ForegroundColor Green 'Creating isolated process...' $pwsh = [System.Diagnostics.Process]::GetCurrentProcess().Path @@ -48,7 +48,6 @@ if (-not $Isolated) { return } } - return } diff --git a/src/Identity.SignIns/Identity.SignIns/test/v1.0-beta/New-MgInvitation.Tests.ps1 b/src/Identity.SignIns/Identity.SignIns/test/v1.0-beta/New-MgInvitation.Tests.ps1 index da32b80221b..8f724ee3f22 100644 --- a/src/Identity.SignIns/Identity.SignIns/test/v1.0-beta/New-MgInvitation.Tests.ps1 +++ b/src/Identity.SignIns/Identity.SignIns/test/v1.0-beta/New-MgInvitation.Tests.ps1 @@ -31,7 +31,7 @@ Describe 'New-MgInvitation' { InviteRedirectUrl = 'https://myapp.contoso.com' } $Invitation = New-MgInvitation @Params - $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation1' + $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation' $Invitation | Should -HaveCount 1 $Invitation.InvitedUserDisplayName | Should -Be $env.Users[0].DisplayName $Invitation.Status | Should -Be 'PendingAcceptance' @@ -59,7 +59,7 @@ Describe 'New-MgInvitation' { InviteRedirectUrl = 'https://myapp.contoso.com' } $Invitation = New-MgInvitation -BodyParameter $Params - $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation1' + $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation' $Invitation | Should -HaveCount 1 $Invitation.InvitedUserDisplayName | Should -Be $env.Users[0].DisplayName $Invitation.Status | Should -Be 'PendingAcceptance' diff --git a/src/Identity.SignIns/Identity.SignIns/test/v1.0/New-MgInvitation.Tests.ps1 b/src/Identity.SignIns/Identity.SignIns/test/v1.0/New-MgInvitation.Tests.ps1 index c30467a04f2..50a293fe2f3 100644 --- a/src/Identity.SignIns/Identity.SignIns/test/v1.0/New-MgInvitation.Tests.ps1 +++ b/src/Identity.SignIns/Identity.SignIns/test/v1.0/New-MgInvitation.Tests.ps1 @@ -31,7 +31,7 @@ Describe 'New-MgInvitation' { InviteRedirectUrl = 'https://myapp.contoso.com' } $Invitation = New-MgInvitation @Params - $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation' + $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation1' $Invitation | Should -HaveCount 1 $Invitation.InvitedUserDisplayName | Should -Be $env.Users[0].DisplayName $Invitation.Status | Should -Be 'PendingAcceptance' @@ -59,7 +59,7 @@ Describe 'New-MgInvitation' { InviteRedirectUrl = 'https://myapp.contoso.com' } $Invitation = New-MgInvitation -BodyParameter $Params - $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation' + $Invitation | Should -BeOfType -ExpectedType 'Microsoft.Graph.PowerShell.Models.MicrosoftGraphInvitation1' $Invitation | Should -HaveCount 1 $Invitation.InvitedUserDisplayName | Should -Be $env.Users[0].DisplayName $Invitation.Status | Should -Be 'PendingAcceptance' diff --git a/tools/TestModule.ps1 b/tools/TestModule.ps1 index 284b735a799..30753cd0ce5 100644 --- a/tools/TestModule.ps1 +++ b/tools/TestModule.ps1 @@ -17,8 +17,11 @@ if(-not $Isolated) { $modulePsd1 = Get-Item -Path (Join-Path $ModulePath "./$ModuleName.psd1") $LocalLoadEnvPS1 = Join-Path $PSScriptRoot 'Tests/loadEnv.ps1' +$AuthModulePSd1 = Join-Path $PSScriptRoot "../src/Authentication/Authentication/artifacts/Microsoft.Graph.Authentication.psd1" +# Import required modules. Import-Module -Name Pester +Import-Module $AuthModulePSd1 Import-Module -Name $modulePsd1.FullName # Replace AutoREST loadEnv.ps1 with our local scipt.