Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
82 changes: 82 additions & 0 deletions src/AzureAuth.Test/AuthModeHelperTest.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
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;

namespace AzureAuth.Test
{
public class AuthModeHelperTest
{
private Mock<IEnv> envMock;
private ILogger logger;
private MemoryTarget logTarget;

[SetUp]
public void SetUp()
{
this.envMock = new Mock<IEnv>();
(this.logger, this.logTarget) = MemoryLogger.Create();
}

[Test]
public void ReadAuthModeFromEnvOrSetDefault_ReturnsDefault_WhenEnvVarIsEmpty()
{
// Arrange
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns(string.Empty);

// Act
var result = AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(envMock.Object, new EventData(), logger);

// Assert
result.Should().BeEquivalentTo(new[] { AuthMode.Default });
}

[Test]
public void ReadAuthModeFromEnvOrSetDefault_ReturnsParsedAuthModes_WhenEnvVarIsValid()
{
// Arrange
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns("Web,Broker");

// Act
var result = AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(envMock.Object, new EventData(), logger);

// Assert
result.Should().BeEquivalentTo(new[] { AuthMode.Web, AuthMode.Broker });
}

[Test]
public void ReadAuthModeFromEnvOrSetDefault_LogsErrorAndReturnsEmpty_WhenEnvVarIsInvalid()
{
// Arrange
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns("InvalidMode");

// Act
var result = AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(envMock.Object, new EventData(), logger);

// Assert
result.Should().BeEmpty();
this.logTarget.Logs.Should().ContainMatch("Invalid value specified for environment variable*");
}

[Test]
public void ReadAuthModeFromEnvOrSetDefault_AddsEventData_WhenEnvVarIsValid()
{
// Arrange
var eventData = new EventData();
envMock.Setup(e => e.Get(It.IsAny<string>())).Returns("Web");

// Act
var result = AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(envMock.Object, eventData, logger);

// Assert
var env_var = $"env_{EnvVars.AuthMode}";
eventData.Properties[env_var.ToLower()].Should().Be("Web");
}
}
}
54 changes: 54 additions & 0 deletions src/AzureAuth/AuthModeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
using Microsoft.Authentication.AzureAuth.Commands;
using Microsoft.Authentication.MSALWrapper;
using Microsoft.Extensions.Logging;
using Microsoft.Office.Lasso.Interfaces;
using Microsoft.Office.Lasso.Telemetry;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Authentication.AzureAuth
{
/// <summary>
/// Helper class for <see cref="AuthMode"/>s.
/// </summary>
public static class AuthModeHelper
{
/// <summary>
/// Get the auth modes from the environment or set the default.
/// </summary>
/// <param name="env">The <see cref="IEnv"/> to use.</param>
/// <param name="eventData">Event data to add the auth mode to.</param>
/// <param name="logger">The <see cref="ILogger"/> to use.</param>
/// <returns>AuthModes.</returns>
public static IEnumerable<AuthMode> ReadAuthModeFromEnvOrSetDefault(IEnv env, EventData eventData, ILogger logger)
Comment thread
mvanchaa marked this conversation as resolved.
Outdated
{
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<AuthMode>();
foreach (var val in authModesFromEnv.Split(','))
{
if (Enum.TryParse<AuthMode>(val, ignoreCase: true, out var mode))
{
result.Add(mode);
}
else
{
logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {CommandAad.AuthModeHelperText}");
return new List<AuthMode>();
}
}

eventData.Add($"env_{EnvVars.AuthMode}", authModesFromEnv);
return result;
}
}
}
14 changes: 12 additions & 2 deletions src/AzureAuth/Commands/Ado/CommandPat.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@ 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;
using Microsoft.Authentication.AzureAuth.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;
Expand Down Expand Up @@ -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<AuthMode> AuthModes { get; set; } = new[] { AuthMode.Default };
private IEnumerable<AuthMode> 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;
Expand Down Expand Up @@ -120,14 +122,22 @@ private ImmutableSortedSet<string> Scopes
/// <param name="logger">The <see cref="ILogger{T}"/> instance that is used for logging.</param>
/// <param name="publicClientAuth">An <see cref="IPublicClientAuth"/>.</param>
/// <param name="eventData">Lasso injected command event data.</param>
/// <param name="env">An <see cref="IEnv"/> to use.</param>
/// <returns>An integer status code. 0 for success and non-zero for failure.</returns>
public int OnExecute(ILogger<CommandPat> logger, IPublicClientAuth publicClientAuth, CommandExecuteEventData eventData)
public int OnExecute(ILogger<CommandPat> 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 ??= AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(env, eventData, logger);
if (!this.AuthModes.Any())
{
return 1;
}

var accessToken = this.AccessToken(publicClientAuth, eventData);
if (accessToken == null)
{
Expand Down
11 changes: 9 additions & 2 deletions src/AzureAuth/Commands/Ado/CommandToken.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<AuthMode> AuthModes { get; set; } = new[] { AuthMode.Default };
private IEnumerable<AuthMode> AuthModes { get; set; }

[Option(CommandAad.DomainOption, Description = DomainOptionDescription)]
private string Domain { get; set; } = AzureAuth.Ado.Constants.PreferredDomain;
Expand Down Expand Up @@ -98,6 +98,13 @@ public int OnExecute(ILogger<CommandToken> logger, IEnv env, ITelemetryService t
return 0;
}

// If command line options for mode are not specified, then use the environment variables.
this.AuthModes ??= AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(env, eventData, logger);
if (!this.AuthModes.Any())
{
return 1;
Comment thread
mvanchaa marked this conversation as resolved.
}

// If no PAT then use AAD AT.
TokenResult token = publicClientAuth.Token(
AzureAuth.Ado.AuthParameters.AdoParameters(this.Tenant),
Expand Down
43 changes: 8 additions & 35 deletions src/AzureAuth/Commands/CommandAad.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,13 @@ public class CommandAad
/// </summary>
public static readonly TimeSpan GlobalTimeout = TimeSpan.FromMinutes(15);

/// <summary>
/// The allowed values for the <see cref="AuthMode"/> option.
/// </summary>
#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";
Expand Down Expand Up @@ -279,9 +282,10 @@ 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 ??= AuthModeHelper.ReadAuthModeFromEnvOrSetDefault(env, eventData, logger);
if (!this.AuthModes.Any())
{
this.logger.LogError($"Invalid value specified for environment variable {EnvVars.AuthMode}. Allowed values are: {AuthModeAllowedValues}");
return false;
}

Expand Down Expand Up @@ -418,36 +422,5 @@ private int GetToken(IPublicClientAuth publicClientAuth)

return 0;
}

/// <summary>
/// Sets the <see cref="AuthMode"/> from the environment variable and sets a default if not set.
/// </summary>
/// <returns>True if authmode is set.</returns>
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<AuthMode>();
foreach(var val in authModesFromEnv.Split(','))
{
if (Enum.TryParse<AuthMode>(val, ignoreCase: true, out var mode))
{
result.Add(mode);
}
else
{
return false;
}
}

this.AuthModes = result;
this.eventData.Add($"env_{EnvVars.AuthMode}", authModesFromEnv);
return true;
}
}
}