diff --git a/CHANGELOG.md b/CHANGELOG.md index 095988cd..f1de09f1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,7 +6,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### Added -- Added support for reading auth mode from the environment variable `AZUREAUTH_MODE` for aad subcommands. +- Added support for reading auth mode from the environment variable `AZUREAUTH_MODE`. ## [0.9.1] - 2024-12-09 ### Changed diff --git a/src/AzureAuth.Test/IEnvExtensionsTest.cs b/src/AzureAuth.Test/IEnvExtensionsTest.cs index 3e89ff0b..3acb16ab 100644 --- a/src/AzureAuth.Test/IEnvExtensionsTest.cs +++ b/src/AzureAuth.Test/IEnvExtensionsTest.cs @@ -6,20 +6,26 @@ namespace AzureAuth.Test using FluentAssertions; using Microsoft.Authentication.AzureAuth; + using Microsoft.Authentication.MSALWrapper; + using Microsoft.Authentication.TestHelper; + using Microsoft.Extensions.Logging; using Microsoft.Office.Lasso.Interfaces; - + using Microsoft.Office.Lasso.Telemetry; using Moq; - + using NLog.Targets; using NUnit.Framework; public class IEnvExtensionsTest { private Mock envMock; + private ILogger logger; + private MemoryTarget logTarget; [SetUp] public void SetUp() { this.envMock = new Mock(); + (this.logger, this.logTarget) = MemoryLogger.Create(); } [TestCase("1", true)] @@ -51,5 +57,44 @@ public void InteractiveAuth_IsEnabledIfEnvVarsAreNotSet() this.envMock.Setup(env => env.Get(It.IsAny())).Returns((string)null); IEnvExtensions.InteractiveAuthDisabled(this.envMock.Object).Should().BeFalse(); } + + [Test] + public void ReadAuthModeFromEnvOrSetDefault_ReturnsDefault_WhenEnvVarIsEmpty() + { + // Arrange + envMock.Setup(e => e.Get(It.IsAny())).Returns(string.Empty); + + // Act + var result = IEnvExtensions.ReadAuthModeFromEnvOrSetDefault(envMock.Object); + + // Assert + result.Should().BeEquivalentTo(new[] { AuthMode.Default }); + } + + [Test] + public void ReadAuthModeFromEnvOrSetDefault_ReturnsParsedAuthModes_WhenEnvVarIsValid() + { + // Arrange + envMock.Setup(e => e.Get(It.IsAny())).Returns("Web,DeviceCode"); + + // Act + var result = IEnvExtensions.ReadAuthModeFromEnvOrSetDefault(envMock.Object); + + // Assert + result.Should().BeEquivalentTo(new[] { AuthMode.Web, AuthMode.DeviceCode }); + } + + [Test] + public void ReadAuthModeFromEnvOrSetDefault_ReturnsEmpty_WhenEnvVarIsInvalid() + { + // Arrange + envMock.Setup(e => e.Get(It.IsAny())).Returns("InvalidMode"); + + // Act + var result = IEnvExtensions.ReadAuthModeFromEnvOrSetDefault(envMock.Object); + + // Assert + result.Should().BeEmpty(); + } } } diff --git a/src/AzureAuth/Commands/Ado/CommandPat.cs b/src/AzureAuth/Commands/Ado/CommandPat.cs index 28494ebc..4dc4c870 100644 --- a/src/AzureAuth/Commands/Ado/CommandPat.cs +++ b/src/AzureAuth/Commands/Ado/CommandPat.cs @@ -7,6 +7,7 @@ namespace Microsoft.Authentication.AzureAuth.Commands.Ado using System.Collections.Generic; using System.Collections.Immutable; using System.IO; + using System.Linq; using McMaster.Extensions.CommandLineUtils; using Microsoft.Authentication.AdoPat; @@ -14,6 +15,7 @@ namespace Microsoft.Authentication.AzureAuth.Commands.Ado using Microsoft.Authentication.MSALWrapper; using Microsoft.Extensions.Logging; using Microsoft.Identity.Client.Extensions.Msal; + using Microsoft.Office.Lasso.Interfaces; using Microsoft.Office.Lasso.Telemetry; using Microsoft.VisualStudio.Services.DelegatedAuthorization; using Microsoft.VisualStudio.Services.OAuth; @@ -92,7 +94,7 @@ private enum OutputMode private string Tenant { get; set; } = AzureAuth.Ado.Constants.Tenant.Microsoft; [Option(CommandAad.ModeOption, CommandAad.AuthModeHelperText, CommandOptionType.MultipleValue)] - private IEnumerable AuthModes { get; set; } = new[] { AuthMode.Default }; + private IEnumerable AuthModes { get; set; } [Option(CommandAad.DomainOption, $"{CommandAad.DomainHelpText}\n[default: {AzureAuth.Ado.Constants.PreferredDomain}]", CommandOptionType.SingleValue)] private string Domain { get; set; } = AzureAuth.Ado.Constants.PreferredDomain; @@ -120,14 +122,23 @@ private ImmutableSortedSet Scopes /// The instance that is used for logging. /// An . /// Lasso injected command event data. + /// An to use. /// An integer status code. 0 for success and non-zero for failure. - public int OnExecute(ILogger logger, IPublicClientAuth publicClientAuth, CommandExecuteEventData eventData) + public int OnExecute(ILogger logger, IPublicClientAuth publicClientAuth, CommandExecuteEventData eventData, IEnv env) { if (!this.ValidOptions(logger)) { return 1; } + // If command line options for mode are not specified, then use the environment variables. + this.AuthModes ??= env.ReadAuthModeFromEnvOrSetDefault(); + if (!this.AuthModes.Any()) + { + logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}"); + return 1; + } + var accessToken = this.AccessToken(publicClientAuth, eventData); if (accessToken == null) { diff --git a/src/AzureAuth/Commands/Ado/CommandToken.cs b/src/AzureAuth/Commands/Ado/CommandToken.cs index 76005ee9..7e57c6c4 100644 --- a/src/AzureAuth/Commands/Ado/CommandToken.cs +++ b/src/AzureAuth/Commands/Ado/CommandToken.cs @@ -5,7 +5,7 @@ namespace Microsoft.Authentication.AzureAuth.Commands.Ado { using System; using System.Collections.Generic; - + using System.Linq; using McMaster.Extensions.CommandLineUtils; using Microsoft.Authentication.AzureAuth.Ado; @@ -52,7 +52,7 @@ public enum OutputMode private string Tenant { get; set; } = AzureAuth.Ado.Constants.Tenant.Microsoft; [Option(CommandAad.ModeOption, CommandAad.AuthModeHelperText, CommandOptionType.MultipleValue)] - private IEnumerable AuthModes { get; set; } = new[] { AuthMode.Default }; + private IEnumerable AuthModes { get; set; } [Option(CommandAad.DomainOption, Description = DomainOptionDescription)] private string Domain { get; set; } = AzureAuth.Ado.Constants.PreferredDomain; @@ -98,6 +98,14 @@ public int OnExecute(ILogger logger, IEnv env, ITelemetryService t return 0; } + // If command line options for mode are not specified, then use the environment variables. + this.AuthModes ??= env.ReadAuthModeFromEnvOrSetDefault(); + if (!this.AuthModes.Any()) + { + logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}"); + return 1; + } + // If no PAT then use AAD AT. TokenResult token = publicClientAuth.Token( AzureAuth.Ado.AuthParameters.AdoParameters(this.Tenant), diff --git a/src/AzureAuth/Commands/CommandAad.cs b/src/AzureAuth/Commands/CommandAad.cs index 6c88e00d..4540cf04 100644 --- a/src/AzureAuth/Commands/CommandAad.cs +++ b/src/AzureAuth/Commands/CommandAad.cs @@ -84,10 +84,13 @@ public class CommandAad /// public static readonly TimeSpan GlobalTimeout = TimeSpan.FromMinutes(15); + /// + /// The allowed values for the option. + /// #if PlatformWindows - private const string AuthModeAllowedValues = "all, iwa, broker, web, devicecode"; + public const string AuthModeAllowedValues = "all, iwa, broker, web, devicecode"; #else - private const string AuthModeAllowedValues = "all, web, devicecode"; + public const string AuthModeAllowedValues = "all, web, devicecode"; #endif private const string ResourceOption = "--resource"; @@ -279,9 +282,11 @@ public bool EvaluateOptions() } } - if (this.AuthModes is null && !this.TrySetAuthModeFromEnvOrDefault()) + // If command line options for mode are not specified, then use the environment variables. + this.AuthModes ??= env.ReadAuthModeFromEnvOrSetDefault(); + if (!this.AuthModes.Any()) { - this.logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {AuthModeAllowedValues}"); + this.logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}"); return false; } @@ -418,36 +423,5 @@ private int GetToken(IPublicClientAuth publicClientAuth) return 0; } - - /// - /// Sets the from the environment variable and sets a default if not set. - /// - /// True if authmode is set. - public bool TrySetAuthModeFromEnvOrDefault() - { - var authModesFromEnv = this.env.Get(EnvVars.AuthMode); - if (string.IsNullOrEmpty(authModesFromEnv)) - { - this.AuthModes = new[] { AuthMode.Default }; - return true; - } - - var result = new List(); - foreach(var val in authModesFromEnv.Split(',')) - { - if (Enum.TryParse(val, ignoreCase: true, out var mode)) - { - result.Add(mode); - } - else - { - return false; - } - } - - this.AuthModes = result; - this.eventData.Add($"env_{EnvVars.AuthMode}", authModesFromEnv); - return true; - } } } diff --git a/src/AzureAuth/IEnvExtensions.cs b/src/AzureAuth/IEnvExtensions.cs index 26857ef0..f67f4143 100644 --- a/src/AzureAuth/IEnvExtensions.cs +++ b/src/AzureAuth/IEnvExtensions.cs @@ -3,7 +3,11 @@ namespace Microsoft.Authentication.AzureAuth { + using Microsoft.Authentication.MSALWrapper; using Microsoft.Office.Lasso.Interfaces; + using Microsoft.Office.Lasso.Telemetry; + using System.Collections.Generic; + using System; /// /// Extension methods to Lasso's interface. @@ -22,5 +26,38 @@ public static bool InteractiveAuthDisabled(this IEnv env) return !string.IsNullOrEmpty(env.Get(EnvVars.NoUser)) || string.Equals(CorextPositiveValue, env.Get(EnvVars.CorextNonInteractive)); } + + /// + /// Get the auth modes from the environment or set the default. + /// + /// The to use. + /// Event data to add the auth mode to. + /// AuthModes. + public static IEnumerable ReadAuthModeFromEnvOrSetDefault(this IEnv env) + { + var authModesFromEnv = env.Get(EnvVars.AuthMode); + + // If auth modes are not specified in the environment, then return the default. + if (string.IsNullOrEmpty(authModesFromEnv)) + { + return new[] { AuthMode.Default }; + } + + var result = new List(); + foreach (var val in authModesFromEnv.Split(',')) + { + if (Enum.TryParse(val, ignoreCase: true, out var mode)) + { + result.Add(mode); + } + else + { + // If the environment variable is not a valid auth mode, then return an empty list. + return new List(); + } + } + + return result; + } } } diff --git a/src/AzureAuth/Program.cs b/src/AzureAuth/Program.cs index 85fd7c69..7a94c4c7 100644 --- a/src/AzureAuth/Program.cs +++ b/src/AzureAuth/Program.cs @@ -68,6 +68,7 @@ private static void Main(string[] args) EnvVars.CloudBuild, EnvVars.NoUser, EnvVars.CorextNonInteractive, + EnvVars.AuthMode, }; TelemetryConfig telemetryConfig = new TelemetryConfig(