Skip to content

Commit

Permalink
[Account]Redirect DeviceCode Info from warning stream to information …
Browse files Browse the repository at this point in the history
…stream in `Connect-AzAccount` (Azure#23665)

* Redirct DeviceCode Info from warning stream to information stream

* add change log

* writehighlightedinformation

* Formmat device code login message

* rename writeinformation

* polish code
  • Loading branch information
BethanyZhou authored Jan 30, 2024
1 parent 53b7258 commit de9f80d
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 10 deletions.
35 changes: 35 additions & 0 deletions src/Accounts/Accounts/Account/ConnectAzureRmAccount.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
using Microsoft.WindowsAzure.Commands.Utilities.Common;
using Microsoft.Azure.PowerShell.Common.Share.Survey;
using Microsoft.Azure.Commands.Profile.Utilities;
using System.Management.Automation.Runspaces;

namespace Microsoft.Azure.Commands.Profile
{
Expand Down Expand Up @@ -249,6 +250,7 @@ protected override IAzureContext DefaultContext
protected override void BeginProcessing()
{
base.BeginProcessing();
ValidateActionRequiredMessageCanBePresented();
if (AzureEnvironment.PublicEnvironments.ContainsKey(EnvironmentName.AzureCloud))
{
_environment = AzureEnvironment.PublicEnvironments[EnvironmentName.AzureCloud];
Expand All @@ -273,11 +275,19 @@ protected override void BeginProcessing()

_writeWarningEvent -= WriteWarningSender;
_writeWarningEvent += WriteWarningSender;
_writeInformationEvent -= WriteInformationSender;
_writeInformationEvent += WriteInformationSender;

// store the original write warning handler, register a thread safe one
AzureSession.Instance.TryGetComponent(WriteWarningKey, out _originalWriteWarning);
AzureSession.Instance.UnregisterComponent<EventHandler<StreamEventArgs>>(WriteWarningKey);
AzureSession.Instance.RegisterComponent(WriteWarningKey, () => _writeWarningEvent);

// store the original write information handler, register a thread safe one
AzureSession.Instance.TryGetComponent(WriteInformationKey, out _originalWriteInformation);
AzureSession.Instance.UnregisterComponent<EventHandler<StreamEventArgs>>(WriteInformationKey);
AzureSession.Instance.RegisterComponent(WriteInformationKey, () => _writeInformationEvent);

// todo: ideally cancellation token should be passed to authentication factory as a parameter
// however AuthenticationFactory.Authenticate does not support it
// so I store it in AzureSession.Instance as a global variable
Expand All @@ -289,11 +299,19 @@ protected override void BeginProcessing()
private event EventHandler<StreamEventArgs> _writeWarningEvent;
private event EventHandler<StreamEventArgs> _originalWriteWarning;

private event EventHandler<StreamEventArgs> _writeInformationEvent;
private event EventHandler<StreamEventArgs> _originalWriteInformation;

private void WriteWarningSender(object sender, StreamEventArgs args)
{
_tasks.Enqueue(new Task(() => this.WriteWarning(args.Message)));
}

private void WriteInformationSender(object sender, StreamEventArgs args)
{
_tasks.Enqueue(new Task(() => this.WriteInformation(args.Message)));
}

protected override void StopProcessing()
{
if (AzureSession.Instance.TryGetComponent("LoginCancellationToken", out CancellationTokenSource cancellationTokenSource))
Expand Down Expand Up @@ -562,6 +580,20 @@ public override void ExecuteCmdlet()
}
}

private void ValidateActionRequiredMessageCanBePresented()
{
if (UseDeviceAuthentication.IsPresent && IsWriteInformationIgnored())
{
throw new ActionPreferenceStopException(Resources.DoNotIgnoreInformationIfUserDeviceAuth);
}
}

private bool IsWriteInformationIgnored()
{
return !MyInvocation.BoundParameters.ContainsKey("InformationAction") && ActionPreference.Ignore.ToString().Equals(SessionState?.PSVariable?.GetValue("InformationPreference", ActionPreference.SilentlyContinue)?.ToString() ?? "") ||
MyInvocation.BoundParameters.TryGetValue("InformationAction", out var value) && ActionPreference.Ignore.ToString().Equals(value?.ToString() ?? "", StringComparison.InvariantCultureIgnoreCase);
}

private string PreProcessAuthScope()
{
string mappedScope = AuthScope;
Expand Down Expand Up @@ -774,6 +806,9 @@ protected override void EndProcessing()
// unregister the thread-safe write warning, because it won't work out of this cmdlet
AzureSession.Instance.UnregisterComponent<EventHandler<StreamEventArgs>>(WriteWarningKey);
AzureSession.Instance.RegisterComponent(WriteWarningKey, () => _originalWriteWarning);
// unregister the thread-safe write information, because it won't work out of this cmdlet
AzureSession.Instance.UnregisterComponent<EventHandler<StreamEventArgs>>(WriteInformationKey);
AzureSession.Instance.RegisterComponent(WriteInformationKey, () => _originalWriteInformation);
}
}
}
5 changes: 4 additions & 1 deletion src/Accounts/Accounts/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@
-->

## Upcoming Release
* Redirected device code login messages from warning stream to information stream if use device authentication in `Connect-AzAccount`.
* Adjusted output format to be more user-friendly for `Get-AzContext/Tenant/Subscription` and `Invoke-AzRestMethod`, including
- ordering and grouping output items to make items easy to find.
- re-prioritizing positions for output properties to highlight valuable properties.
* Upgraded the reference of Azure PowerShell Common to 1.3.90-preview.
* Upgraded Azure.Identity to 1.10.3 [#23018].
- Renamed token cache from `msal.cache` to `msal.cache.cae` or `masl.cache.nocae`.
* Enabled Continue Access Evaluation (CAE) for all Service Principals login methods.
* Supported signing in with Microsoft Account (MSA) via Web Account Manager (WAM). Enable it by `Set-AzConfig -EnableLoginByWam $true`.
* Adjusted output format to be more user-friendly for `Get-AzContext/Tenant/Subscription` and `Invoke-AzRestMethod`.
* Fixed the multiple `x-ms-unique-id` values issue.

## Version 2.15.0
Expand Down
9 changes: 9 additions & 0 deletions src/Accounts/Accounts/Properties/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions src/Accounts/Accounts/Properties/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -595,4 +595,7 @@
<data name="ProfileCredentialsWriteWarning" xml:space="preserve">
<value>Personally identifiable information and confidential data may be written to the file located at '{0}'. Please ensure that appropriate access controls are assigned to the saved file.</value>
</data>
<data name="DoNotIgnoreInformationIfUserDeviceAuth" xml:space="preserve">
<value>Please do not set InformationAction or $InformationPreference to Ignore if you want to use device code authentication.</value>
</data>
</root>
37 changes: 28 additions & 9 deletions src/Accounts/Authenticators/DeviceCodeAuthenticator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,6 @@
// limitations under the License.
// ----------------------------------------------------------------------------------

using System;
using System.Threading;
using System.Threading.Tasks;

using Azure.Core;
using Azure.Identity;

Expand All @@ -24,6 +20,12 @@
using Microsoft.Azure.Commands.Common.Authentication;
using Microsoft.Azure.Commands.Common.Authentication.Abstractions;
using Microsoft.Azure.Commands.ResourceManager.Common;
using Microsoft.WindowsAzure.Commands.Common;

using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.Azure.PowerShell.Authenticators
{
Expand Down Expand Up @@ -64,7 +66,7 @@ public override Task<IAccessToken> Authenticate(AuthenticationParameters paramet

private Task DeviceCodeFunc(DeviceCodeInfo info, CancellationToken cancellation)
{
WriteWarning(info.Message);
WriteInfomartion(info.Message, info.UserCode);
return Task.CompletedTask;
}

Expand All @@ -73,12 +75,29 @@ public override bool CanAuthenticate(AuthenticationParameters parameters)
return (parameters as DeviceCodeParameters) != null;
}

private void WriteWarning(string message)

private void WriteInfomartion(string message, string userCode)
{
EventHandler<StreamEventArgs> writeWarningEvent;
if (AzureSession.Instance.TryGetComponent(AzureRMCmdlet.WriteWarningKey, out writeWarningEvent))

var loginInfo = new StringBuilder();
string LoginToAzurePhrase = $"{PSStyle.Bold}{PSStyle.BackgroundColor.Blue}[Login to Azure]{PSStyle.Reset} ";
loginInfo.Append(LoginToAzurePhrase);

if (!string.IsNullOrEmpty(userCode))
{
var formattedUserCode = $"{PSStyle.Underline}{userCode}{PSStyle.Reset}";
var formattedMessage = message.Replace(userCode, formattedUserCode);
loginInfo.Append(formattedMessage);
}
else
{
loginInfo.Append(message);
}

EventHandler<StreamEventArgs> writeInforamtionEvent;
if (AzureSession.Instance.TryGetComponent(AzureRMCmdlet.WriteInformationKey, out writeInforamtionEvent))
{
writeWarningEvent(this, new StreamEventArgs() { Message = message });
writeInforamtionEvent(this, new StreamEventArgs() { Message = loginInfo.ToString() });
}
}
}
Expand Down

0 comments on commit de9f80d

Please sign in to comment.