1
0
Форкнуть 0
Standardize and clean up the CLI code.

 - [x] Use a common style for all p/invoke callsites.
 - [x] Properly indent and scope `switch` statements.
 - [x] Ensure use of correct comparer types.
 - [x] Adopt use of inline declaration where suitable.
 - [x] Adopt use of `var` where suitable / avoid use of `var` where not suitable.
 - [x] Clean up `using` statements in file headers.
 - [x] Adopt `using static` statements where suitable.
 - [x] Implement debugger display attributes where needed.
This commit is contained in:
J Wyman ∞ 2018-08-02 16:41:49 -04:00 коммит произвёл J Wyman
Родитель 92f0b9f800
Коммит 656851d8e6
10 изменённых файлов: 635 добавлений и 444 удалений

Просмотреть файл

@ -30,7 +30,7 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Alm.Authentication;
using static System.StringComparer;
using Git = Microsoft.Alm.Authentication.Git;
namespace Microsoft.Alm.Cli
@ -227,10 +227,13 @@ namespace Microsoft.Alm.Cli
WriteLine($" Authority = {operationArguments.Authority}");
WriteLine($" CustomNamespace = {operationArguments.CustomNamespace}");
WriteLine($" Interactivity = {operationArguments.Interactivity}");
WriteLine($" ParentHwnd = {operationArguments.ParentHwnd}");
WriteLine($" PreserveCredentials = {operationArguments.PreserveCredentials}");
WriteLine($" QueryUri = {operationArguments.QueryUri}");
WriteLine($" TargetUri = {operationArguments.TargetUri}");
WriteLine($" ActualUri = {operationArguments.TargetUri.ActualUri}");
WriteLine($" TokenDuration = {operationArguments.TokenDuration}");
WriteLine($" UrlOverride = {operationArguments.UrlOverride}");
WriteLine($" UseConfigLocal = {operationArguments.UseConfigLocal}");
WriteLine($" UseConfigSystem = {operationArguments.UseConfigSystem}");
WriteLine($" UseHttpPath = {operationArguments.UseHttpPath}");
@ -263,61 +266,54 @@ namespace Microsoft.Alm.Cli
goto error_parse;
}
using (var stdin = InStream)
var operationArguments = new OperationArguments(_context);
operationArguments.SetTargetUri(uri);
if (operationArguments.TargetUri is null)
{
var operationArguments = new OperationArguments(_context)
{
QueryUri = uri
};
Task.Run(async () =>
{
await operationArguments.ReadInput(stdin);
if (operationArguments.TargetUri is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
BaseAuthentication authentication = await CreateAuthentication(operationArguments);
switch (operationArguments.Authority)
{
default:
case AuthorityType.Basic:
_context.Trace.WriteLine($"deleting basic credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.AzureDirectory:
case AuthorityType.MicrosoftAccount:
_context.Trace.WriteLine($"deleting VSTS credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.GitHub:
_context.Trace.WriteLine($"deleting GitHub credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Ntlm:
_context.Trace.WriteLine($"deleting NTLM credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Bitbucket:
_context.Trace.WriteLine($"deleting Bitbucket credentials for '{operationArguments.Username}@{operationArguments.TargetUri}'.");
break;
}
await authentication.DeleteCredentials(operationArguments.TargetUri, operationArguments.Username);
}).Wait();
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
Task.Run(async () =>
{
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
BaseAuthentication authentication = await CreateAuthentication(operationArguments);
switch (operationArguments.Authority)
{
default:
case AuthorityType.Basic:
_context.Trace.WriteLine($"deleting basic credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.AzureDirectory:
case AuthorityType.MicrosoftAccount:
_context.Trace.WriteLine($"deleting VSTS credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.GitHub:
_context.Trace.WriteLine($"deleting GitHub credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Ntlm:
_context.Trace.WriteLine($"deleting NTLM credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Bitbucket:
_context.Trace.WriteLine($"deleting Bitbucket credentials for '{operationArguments.Username}@{operationArguments.TargetUri}'.");
break;
}
await authentication.DeleteCredentials(operationArguments.TargetUri, operationArguments.Username);
}).Wait();
return;
error_parse:
@ -336,95 +332,95 @@ namespace Microsoft.Alm.Cli
internal void Erase()
{
// parse the operations arguments from stdin (this is how git sends commands)
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
Task.Run(async () =>
{
var operationArguments = new OperationArguments(_context);
Task.Run(async () =>
// parse the operations arguments from stdin (this is how git sends commands)
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
{
await operationArguments.ReadInput(stdin);
}
if (operationArguments.TargetUri is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
if (operationArguments.TargetUri is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process, but only if
// an override hasn't been set which would override the git-remote details.
if (string.IsNullOrEmpty(operationArguments.UrlOverride))
{
ReadGitRemoteDetails(operationArguments);
}
// Read the details of any git-remote-http(s).exe parent process, but only if
// an override hasn't been set which would override the git-remote details.
if (string.IsNullOrEmpty(operationArguments.UrlOverride))
{
ReadGitRemoteDetails(operationArguments);
}
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
if (operationArguments.PreserveCredentials)
{
_context.Trace.WriteLine($"{KeyTypeName(KeyType.PreserveCredentials)} = true, canceling erase request.");
return;
}
if (operationArguments.PreserveCredentials)
{
_context.Trace.WriteLine($"{KeyTypeName(KeyType.PreserveCredentials)} = true, canceling erase request.");
return;
}
await DeleteCredentials(operationArguments);
}).Wait();
}
await DeleteCredentials(operationArguments);
}).Wait();
}
internal void Get()
{
// Parse the operations arguments from stdin (this is how git sends commands)
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
Task.Run(async () =>
{
var operationArguments = new OperationArguments(_context);
Task.Run(async () =>
// Parse the operations arguments from stdin (this is how git sends commands)
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
{
await operationArguments.ReadInput(stdin);
}
if (operationArguments.TargetUri is null)
if (operationArguments.TargetUri is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process, but only if
// an override hasn't been set which would override the git-remote details.
if (string.IsNullOrEmpty(operationArguments.UrlOverride))
{
ReadGitRemoteDetails(operationArguments);
}
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
Credential credentials;
if ((credentials = await QueryCredentials(operationArguments)) == null)
{
Exit(-1, LogonFailedMessage);
}
else
{
using (var stdout = OutStream)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
operationArguments.WriteToStream(stdout);
}
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process, but only if
// an override hasn't been set which would override the git-remote details.
if (string.IsNullOrEmpty(operationArguments.UrlOverride))
{
ReadGitRemoteDetails(operationArguments);
}
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
Credential credentials;
if ((credentials = await QueryCredentials(operationArguments)) == null)
{
Exit(-1, LogonFailedMessage);
}
else
{
using (var stdout = OutStream)
{
operationArguments.WriteToStream(stdout);
}
}
}).Wait();
}
}
}).Wait();
}
internal void PrintHelpMessage()
@ -470,92 +466,92 @@ namespace Microsoft.Alm.Cli
internal void Store()
{
// parse the operations arguments from stdin (this is how git sends commands)
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
Task.Run(async () =>
{
var operationArguments = new OperationArguments(_context);
Task.Run(async () =>
// parse the operations arguments from stdin (this is how git sends commands)
// see: https://www.kernel.org/pub/software/scm/git/docs/technical/api-credentials.html
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
{
await operationArguments.ReadInput(stdin);
}
if (operationArguments.TargetUri is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
if (operationArguments.Username is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.Username));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
if (operationArguments.TargetUri is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.TargetUri));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
if (operationArguments.Username is null)
{
var inner = new ArgumentNullException(nameof(operationArguments.Username));
throw new ArgumentException(inner.Message, nameof(operationArguments), inner);
}
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Load operation arguments.
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process, but only if
// an override hasn't been set which would override the git-remote details.
if (string.IsNullOrEmpty(operationArguments.UrlOverride))
{
ReadGitRemoteDetails(operationArguments);
}
// Read the details of any git-remote-http(s).exe parent process, but only if
// an override hasn't been set which would override the git-remote details.
if (string.IsNullOrEmpty(operationArguments.UrlOverride))
{
ReadGitRemoteDetails(operationArguments);
}
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
// Set the parent window handle.
ParentHwnd = operationArguments.ParentHwnd;
var credentials = operationArguments.Credentials;
BaseAuthentication authentication = await CreateAuthentication(operationArguments); ;
var credentials = operationArguments.Credentials;
BaseAuthentication authentication = await CreateAuthentication(operationArguments); ;
switch (operationArguments.Authority)
{
default:
case AuthorityType.Basic:
Trace.WriteLine($"storing basic credentials for '{operationArguments.TargetUri}'.");
break;
switch (operationArguments.Authority)
{
default:
case AuthorityType.Basic:
Trace.WriteLine($"storing basic credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Bitbucket:
Trace.WriteLine($"storing Bitbucket credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Bitbucket:
Trace.WriteLine($"storing Bitbucket credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.AzureDirectory:
case AuthorityType.MicrosoftAccount:
Trace.WriteLine($"storing VSTS credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.AzureDirectory:
case AuthorityType.MicrosoftAccount:
Trace.WriteLine($"storing VSTS credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.GitHub:
Trace.WriteLine($"storing GitHub credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.GitHub:
Trace.WriteLine($"storing GitHub credentials for '{operationArguments.TargetUri}'.");
break;
case AuthorityType.Ntlm:
Trace.WriteLine($"storing NTLM credentials for '{operationArguments.TargetUri}'.");
break;
}
case AuthorityType.Ntlm:
Trace.WriteLine($"storing NTLM credentials for '{operationArguments.TargetUri}'.");
break;
}
await authentication.SetCredentials(operationArguments.TargetUri, credentials);
}).Wait();
}
await authentication.SetCredentials(operationArguments.TargetUri, credentials);
}).Wait();
}
[STAThread]
private static void Main(string[] args)
private static void Main(string[ ] args)
{
var program = new Program(RuntimeContext.Default);
program.Run(args);
}
private void Run(string[] args)
private void Run(string[ ] args)
{
try
{
EnableDebugTrace();
if (args.Length == 0
|| string.Equals(args[0], "--help", StringComparison.OrdinalIgnoreCase)
|| string.Equals(args[0], "-h", StringComparison.OrdinalIgnoreCase)
|| OrdinalIgnoreCase.Equals(args[0], "--help")
|| OrdinalIgnoreCase.Equals(args[0], "-h")
|| args[0].Contains('?'))
{
PrintHelpMessage();
@ -568,7 +564,7 @@ namespace Microsoft.Alm.Cli
PrintArgs(args);
// List of `arg` => method associations (case-insensitive).
var actions = new Dictionary<string, Action>(StringComparer.OrdinalIgnoreCase)
var actions = new Dictionary<string, Action>(OrdinalIgnoreCase)
{
{ CommandApprove, Store },
{ CommandClear, Clear },

Просмотреть файл

@ -34,17 +34,7 @@ namespace Microsoft.Alm.Cli
/// Attempt to detect the authority automatically, fallback to `<see cref="Basic"/>` if unable
/// to detect an authority.
/// </summary>
Auto,
/// <summary>
/// Basic username and password scheme.
/// </summary>
Basic,
/// <summary>
/// Username and password scheme using Microsoft's Live system.
/// </summary>
MicrosoftAccount,
Auto = 0,
/// <summary>
/// Azure Directory Authentication based, including support for ADFS.
@ -52,15 +42,25 @@ namespace Microsoft.Alm.Cli
AzureDirectory,
/// <summary>
/// GitHub authentication.
/// Basic username and password scheme.
/// </summary>
GitHub,
Basic,
/// <summary>
/// Bitbucket authentication.
/// </summary>
Bitbucket,
/// <summary>
/// GitHub authentication.
/// </summary>
GitHub,
/// <summary>
/// Username and password scheme using Microsoft's Live system.
/// </summary>
MicrosoftAccount,
/// <summary>
/// Windows network integrated authentication layer.
/// </summary>

Просмотреть файл

@ -29,6 +29,7 @@ using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Alm.Authentication;
using static Microsoft.Alm.NativeMethods;
using Bitbucket = Atlassian.Bitbucket.Authentication;
namespace Microsoft.Alm.Cli
@ -64,39 +65,58 @@ namespace Microsoft.Alm.Cli
string accessToken = null;
var fileAccessFlags = NativeMethods.FileAccess.GenericRead | NativeMethods.FileAccess.GenericWrite;
var fileAttributes = NativeMethods.FileAttributes.Normal;
var fileCreationDisposition = NativeMethods.FileCreationDisposition.OpenExisting;
var fileShareFlags = NativeMethods.FileShare.Read | NativeMethods.FileShare.Write;
var fileAccessFlags = FileAccess.GenericRead
| FileAccess.GenericWrite;
var fileAttributes = FileAttributes.Normal;
var fileCreationDisposition = FileCreationDisposition.OpenExisting;
var fileShareFlags = FileShare.Read
| FileShare.Write;
using (var stdout = NativeMethods.CreateFile(NativeMethods.ConsoleOutName, fileAccessFlags, fileShareFlags,
IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero))
using (var stdout = CreateFile(fileName: ConsoleOutName,
desiredAccess: fileAccessFlags,
shareMode: fileShareFlags,
securityAttributes: IntPtr.Zero,
creationDisposition: fileCreationDisposition,
flagsAndAttributes: fileAttributes,
templateFile: IntPtr.Zero))
using (var stdin = CreateFile(fileName: ConsoleInName,
desiredAccess: fileAccessFlags,
shareMode: fileShareFlags,
securityAttributes: IntPtr.Zero,
creationDisposition: fileCreationDisposition,
flagsAndAttributes: fileAttributes,
templateFile: IntPtr.Zero))
{
using (var stdin = NativeMethods.CreateFile(NativeMethods.ConsoleInName, fileAccessFlags, fileShareFlags,
IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero))
buffer.AppendLine()
.Append(title)
.Append(" OAuth Access Token: ");
if (!WriteConsole(buffer: buffer,
consoleOutputHandle: stdout,
numberOfCharsToWrite: (uint)buffer.Length,
numberOfCharsWritten: out written,
reserved: IntPtr.Zero))
{
buffer.AppendLine()
.Append(title)
.Append(" OAuth Access Token: ");
if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ").");
}
buffer.Clear();
// read input from the user
if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to read from standard input (" + NativeMethods.Win32Error.GetText(error) + ").");
}
accessToken = buffer.ToString(0, (int)read);
accessToken = accessToken.Trim(program.NewLineChars);
var error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to write to standard output (" + Win32Error.GetText(error) + ").");
}
buffer.Clear();
// read input from the user
if (!ReadConsole(buffer: buffer,
consoleInputHandle: stdin,
numberOfCharsToRead: BufferReadSize,
numberOfCharsRead: out read,
reserved: IntPtr.Zero))
{
var error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to read from standard input (" + Win32Error.GetText(error) + ").");
}
accessToken = buffer.ToString(0, (int)read);
accessToken = accessToken.Trim(program.NewLineChars);
}
return accessToken != null;
}
}

Просмотреть файл

@ -50,8 +50,9 @@ namespace Microsoft.Alm.Cli
throw new ArgumentException(innerException.Message, nameof(operationArguments), innerException);
}
var secretsNamespace = operationArguments.CustomNamespace ?? Program.SecretsNamespace;
BaseAuthentication authority = null;
NtlmSupport basicNtlmSupport = NtlmSupport.Auto;
string secretsNamespace = operationArguments.CustomNamespace ?? Program.SecretsNamespace;
var basicCredentialCallback = (operationArguments.UseModalUi)
? new AcquireCredentialsDelegate(program.ModalPromptForCredentials)
@ -77,130 +78,144 @@ namespace Microsoft.Alm.Cli
? new Github.Authentication.AcquireAuthenticationCodeDelegate(githubPrompts.AuthenticationCodeModalPrompt)
: new Github.Authentication.AcquireAuthenticationCodeDelegate(program.GitHubAuthCodePrompt);
NtlmSupport basicNtlmSupport = NtlmSupport.Auto;
switch (operationArguments.Authority)
{
case AuthorityType.Auto:
program.Trace.WriteLine($"detecting authority type for '{operationArguments.TargetUri}'.");
// Detect the authority.
authority = await Vsts.Authentication.GetAuthentication(program.Context,
operationArguments.TargetUri,
Program.VstsCredentialScope,
new SecretStore(program.Context, secretsNamespace, Vsts.Authentication.UriNameConversion))
?? Github.Authentication.GetAuthentication(program.Context,
operationArguments.TargetUri,
Program.GitHubCredentialScope,
new SecretStore(program.Context, secretsNamespace, Secret.UriToName),
githubCredentialCallback,
githubAuthcodeCallback,
null)
?? Bitbucket.Authentication.GetAuthentication(program.Context,
operationArguments.TargetUri,
new SecretStore(program.Context, secretsNamespace, Secret.UriToIdentityUrl),
bitbucketCredentialCallback,
bitbucketOauthCallback);
if (authority != null)
{
// Set the authority type based on the returned value.
if (authority is Vsts.MsaAuthentication)
program.Trace.WriteLine($"detecting authority type for '{operationArguments.TargetUri}'.");
// Detect the authority.
authority = await Vsts.Authentication.GetAuthentication(program.Context,
operationArguments.TargetUri,
Program.VstsCredentialScope,
new SecretStore(program.Context, secretsNamespace, Vsts.Authentication.UriNameConversion))
?? Github.Authentication.GetAuthentication(program.Context,
operationArguments.TargetUri,
Program.GitHubCredentialScope,
new SecretStore(program.Context, secretsNamespace, Secret.UriToName),
githubCredentialCallback,
githubAuthcodeCallback,
null)
?? Bitbucket.Authentication.GetAuthentication(program.Context,
operationArguments.TargetUri,
new SecretStore(program.Context, secretsNamespace, Secret.UriToIdentityUrl),
bitbucketCredentialCallback,
bitbucketOauthCallback);
if (authority != null)
{
operationArguments.Authority = AuthorityType.MicrosoftAccount;
goto case AuthorityType.MicrosoftAccount;
}
else if (authority is Vsts.AadAuthentication)
{
operationArguments.Authority = AuthorityType.AzureDirectory;
goto case AuthorityType.AzureDirectory;
}
else if (authority is Github.Authentication)
{
operationArguments.Authority = AuthorityType.GitHub;
goto case AuthorityType.GitHub;
}
else if (authority is Bitbucket.Authentication)
{
operationArguments.Authority = AuthorityType.Bitbucket;
goto case AuthorityType.Bitbucket;
// Set the authority type based on the returned value.
if (authority is Vsts.MsaAuthentication)
{
operationArguments.Authority = AuthorityType.MicrosoftAccount;
goto case AuthorityType.MicrosoftAccount;
}
else if (authority is Vsts.AadAuthentication)
{
operationArguments.Authority = AuthorityType.AzureDirectory;
goto case AuthorityType.AzureDirectory;
}
else if (authority is Github.Authentication)
{
operationArguments.Authority = AuthorityType.GitHub;
goto case AuthorityType.GitHub;
}
else if (authority is Bitbucket.Authentication)
{
operationArguments.Authority = AuthorityType.Bitbucket;
goto case AuthorityType.Bitbucket;
}
}
}
goto default;
case AuthorityType.AzureDirectory:
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Azure Directory.");
if (authority is null)
{
Guid tenantId = Guid.Empty;
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Azure Directory.");
// Get the identity of the tenant.
var result = await Vsts.Authentication.DetectAuthority(program.Context, operationArguments.TargetUri);
if (result.HasValue)
if (authority is null)
{
tenantId = result.Value;
Guid tenantId = Guid.Empty;
// Get the identity of the tenant.
var result = await Vsts.Authentication.DetectAuthority(program.Context, operationArguments.TargetUri);
if (result.HasValue)
{
tenantId = result.Value;
}
// Create the authority object.
authority = new Vsts.AadAuthentication(program.Context,
tenantId,
operationArguments.VstsTokenScope,
new SecretStore(program.Context, secretsNamespace, Vsts.AadAuthentication.UriNameConversion));
}
// Create the authority object.
authority = new Vsts.AadAuthentication(program.Context,
tenantId,
operationArguments.VstsTokenScope,
new SecretStore(program.Context, secretsNamespace, Vsts.AadAuthentication.UriNameConversion));
// Return the allocated authority or a generic AAD backed VSTS authentication object.
return authority;
}
// Return the allocated authority or a generic AAD backed VSTS authentication object.
return authority;
case AuthorityType.Basic:
// Enforce basic authentication only.
basicNtlmSupport = NtlmSupport.Never;
{
// Enforce basic authentication only.
basicNtlmSupport = NtlmSupport.Never;
}
goto default;
case AuthorityType.GitHub:
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is GitHub.");
{
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is GitHub.");
// Return a GitHub authentication object.
return authority ?? new Github.Authentication(program.Context,
operationArguments.TargetUri,
Program.GitHubCredentialScope,
new SecretStore(program.Context, secretsNamespace, Secret.UriToName),
githubCredentialCallback,
githubAuthcodeCallback,
null);
// Return a GitHub authentication object.
return authority ?? new Github.Authentication(program.Context,
operationArguments.TargetUri,
Program.GitHubCredentialScope,
new SecretStore(program.Context, secretsNamespace, Secret.UriToName),
githubCredentialCallback,
githubAuthcodeCallback,
null);
}
case AuthorityType.Bitbucket:
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Bitbucket.");
{
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Bitbucket.");
// Return a Bitbucket authentication object.
return authority ?? new Bitbucket.Authentication(program.Context,
new SecretStore(program.Context, secretsNamespace, Secret.UriToIdentityUrl),
bitbucketCredentialCallback,
bitbucketOauthCallback);
// Return a Bitbucket authentication object.
return authority ?? new Bitbucket.Authentication(program.Context,
new SecretStore(program.Context, secretsNamespace, Secret.UriToIdentityUrl),
bitbucketCredentialCallback,
bitbucketOauthCallback);
}
case AuthorityType.MicrosoftAccount:
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Microsoft Live.");
{
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Microsoft Live.");
// Return the allocated authority or a generic MSA backed VSTS authentication object.
return authority ?? new Vsts.MsaAuthentication(program.Context,
operationArguments.VstsTokenScope,
new SecretStore(program.Context, secretsNamespace, Vsts.MsaAuthentication.UriNameConversion));
// Return the allocated authority or a generic MSA backed VSTS authentication object.
return authority ?? new Vsts.MsaAuthentication(program.Context,
operationArguments.VstsTokenScope,
new SecretStore(program.Context, secretsNamespace, Vsts.MsaAuthentication.UriNameConversion));
}
case AuthorityType.Ntlm:
// Enforce NTLM authentication only.
basicNtlmSupport = NtlmSupport.Always;
{
// Enforce NTLM authentication only.
basicNtlmSupport = NtlmSupport.Always;
}
goto default;
default:
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is basic with NTLM={basicNtlmSupport}.");
{
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is basic with NTLM={basicNtlmSupport}.");
// Return a generic username + password authentication object.
return authority ?? new BasicAuthentication(program.Context,
new SecretStore(program.Context, secretsNamespace, Secret.UriToIdentityUrl),
basicNtlmSupport,
basicCredentialCallback,
null);
// Return a generic username + password authentication object.
return authority ?? new BasicAuthentication(program.Context,
new SecretStore(program.Context, secretsNamespace, Secret.UriToIdentityUrl),
basicNtlmSupport,
basicCredentialCallback,
null);
}
}
}
@ -217,24 +232,32 @@ namespace Microsoft.Alm.Cli
{
default:
case AuthorityType.Basic:
program.Trace.WriteLine($"deleting basic credentials for '{operationArguments.TargetUri}'.");
return await authentication.DeleteCredentials(operationArguments.TargetUri);
{
program.Trace.WriteLine($"deleting basic credentials for '{operationArguments.TargetUri}'.");
return await authentication.DeleteCredentials(operationArguments.TargetUri);
}
case AuthorityType.AzureDirectory:
case AuthorityType.MicrosoftAccount:
program.Trace.WriteLine($"deleting VSTS credentials for '{operationArguments.TargetUri}'.");
var vstsAuth = authentication as Vsts.Authentication;
return await vstsAuth.DeleteCredentials(operationArguments.TargetUri);
{
program.Trace.WriteLine($"deleting VSTS credentials for '{operationArguments.TargetUri}'.");
var vstsAuth = authentication as Vsts.Authentication;
return await vstsAuth.DeleteCredentials(operationArguments.TargetUri);
}
case AuthorityType.GitHub:
program.Trace.WriteLine($"deleting GitHub credentials for '{operationArguments.TargetUri}'.");
var ghAuth = authentication as Github.Authentication;
return await ghAuth.DeleteCredentials(operationArguments.TargetUri);
{
program.Trace.WriteLine($"deleting GitHub credentials for '{operationArguments.TargetUri}'.");
var ghAuth = authentication as Github.Authentication;
return await ghAuth.DeleteCredentials(operationArguments.TargetUri);
}
case AuthorityType.Bitbucket:
program.Trace.WriteLine($"deleting Bitbucket credentials for '{operationArguments.TargetUri}'.");
var bbAuth = authentication as Bitbucket.Authentication;
return await bbAuth.DeleteCredentials(operationArguments.TargetUri, operationArguments.Username);
{
program.Trace.WriteLine($"deleting Bitbucket credentials for '{operationArguments.TargetUri}'.");
var bbAuth = authentication as Bitbucket.Authentication;
return await bbAuth.DeleteCredentials(operationArguments.TargetUri, operationArguments.Username);
}
}
}
@ -248,15 +271,9 @@ namespace Microsoft.Alm.Cli
program.Trace.WriteException(exception, path, line, name);
program.LogEvent(exception.ToString(), EventLogEntryType.Error);
string message;
if (!string.IsNullOrWhiteSpace(exception.Message))
{
message = $"{exception.GetType().Name} encountered.\n {exception.Message}";
}
else
{
message = $"{exception.GetType().Name} encountered.";
}
string message = string.IsNullOrWhiteSpace(exception.Message)
? $"{exception.GetType().Name} encountered."
: $"{exception.GetType().Name} encountered.\n {exception.Message}";
program.Die(message, path, line, name);
}
@ -430,7 +447,7 @@ namespace Microsoft.Alm.Cli
operationArguments.Authority = AuthorityType.GitHub;
}
else if (Program.ConfigKeyComparer.Equals(value, "Atlassian")
|| Program.ConfigKeyComparer.Equals(value, "Bitbucket"))
|| Program.ConfigKeyComparer.Equals(value, "Bitbucket"))
{
operationArguments.Authority = AuthorityType.Bitbucket;
}
@ -491,10 +508,10 @@ namespace Microsoft.Alm.Cli
}
else if (operationArguments.EnvironmentVariables.TryGetValue("GCM_PRESERVE_CREDS", out value))
{
if (StringComparer.OrdinalIgnoreCase.Equals(value, "true")
|| StringComparer.OrdinalIgnoreCase.Equals(value, "yes")
|| StringComparer.OrdinalIgnoreCase.Equals(value, "1")
|| StringComparer.OrdinalIgnoreCase.Equals(value, "on"))
if (Program.ConfigValueComparer.Equals(value, "true")
|| Program.ConfigValueComparer.Equals(value, "yes")
|| Program.ConfigValueComparer.Equals(value, "1")
|| Program.ConfigValueComparer.Equals(value, "on"))
{
program.Trace.WriteLine($"GCM_PRESERVE_CREDS = '{yesno}'.");
@ -540,8 +557,7 @@ namespace Microsoft.Alm.Cli
// Check the git-config http.proxy setting just-in-case.
else
{
Git.Configuration.Entry entry;
if (operationArguments.GitConfiguration.TryGetEntry("http", operationArguments.QueryUri, "proxy", out entry)
if (operationArguments.GitConfiguration.TryGetEntry("http", operationArguments.QueryUri, "proxy", out Git.Configuration.Entry entry)
&& !string.IsNullOrWhiteSpace(entry.Value))
{
program.Trace.WriteLine($"http.proxy = '{entry.Value}'.");
@ -648,7 +664,7 @@ namespace Microsoft.Alm.Cli
catch { /* squelch */ }
}
public static void PrintArgs(Program program, string[] args)
public static void PrintArgs(Program program, string[ ] args)
{
if (program is null)
throw new ArgumentNullException(nameof(program));
@ -695,6 +711,8 @@ namespace Microsoft.Alm.Cli
BaseAuthentication authentication = await program.CreateAuthentication(operationArguments);
Credential credentials = null;
program.Trace.WriteLine($"querying '{operationArguments.Authority}' for credentials.");
switch (operationArguments.Authority)
{
default:
@ -913,8 +931,8 @@ namespace Microsoft.Alm.Cli
goto parse_localval;
}
// Parse the value into a bool.
parse_localval:
// Parse the value into a bool.
parse_localval:
// An empty value is unset / should not be there, so treat it as if it isn't.
if (string.IsNullOrWhiteSpace(localVal))
@ -959,9 +977,8 @@ namespace Microsoft.Alm.Cli
var envars = operationArguments.EnvironmentVariables;
// Look for an entry in the environment variables.
string localVal;
if (!string.IsNullOrWhiteSpace(environKey)
&& envars.TryGetValue(environKey, out localVal)
&& envars.TryGetValue(environKey, out string localVal)
&& !string.IsNullOrWhiteSpace(localVal))
{
value = localVal;
@ -971,9 +988,8 @@ namespace Microsoft.Alm.Cli
Git.Configuration config = operationArguments.GitConfiguration;
// Look for an entry in the git config.
Git.Configuration.Entry entry;
if (!string.IsNullOrWhiteSpace(configKey)
&& config.TryGetEntry(Program.ConfigPrefix, operationArguments.QueryUri, configKey, out entry)
&& config.TryGetEntry(Program.ConfigPrefix, operationArguments.QueryUri, configKey, out Git.Configuration.Entry entry)
&& !string.IsNullOrWhiteSpace(entry.Value))
{
value = entry.Value;

Просмотреть файл

@ -25,14 +25,12 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Alm.Authentication;
using Microsoft.Win32.SafeHandles;
using Git = Microsoft.Alm.Authentication.Git;
using static Microsoft.Alm.NativeMethods;
namespace Microsoft.Alm.Cli
{
@ -50,18 +48,32 @@ namespace Microsoft.Alm.Cli
titleMessage = titleMessage ?? "Please enter your credentials for ";
StringBuilder buffer = new StringBuilder(BufferReadSize);
var buffer = new StringBuilder(BufferReadSize);
uint read = 0;
uint written = 0;
NativeMethods.ConsoleMode consoleMode = 0;
NativeMethods.FileAccess fileAccessFlags = NativeMethods.FileAccess.GenericRead | NativeMethods.FileAccess.GenericWrite;
NativeMethods.FileAttributes fileAttributes = NativeMethods.FileAttributes.Normal;
NativeMethods.FileCreationDisposition fileCreationDisposition = NativeMethods.FileCreationDisposition.OpenExisting;
NativeMethods.FileShare fileShareFlags = NativeMethods.FileShare.Read | NativeMethods.FileShare.Write;
ConsoleMode consoleMode = 0;
var fileAccessFlags = NativeMethods.FileAccess.GenericRead
| NativeMethods.FileAccess.GenericWrite;
var fileAttributes = NativeMethods.FileAttributes.Normal;
var fileCreationDisposition = FileCreationDisposition.OpenExisting;
var fileShareFlags = NativeMethods.FileShare.Read
| NativeMethods.FileShare.Write;
using (SafeFileHandle stdout = NativeMethods.CreateFile(NativeMethods.ConsoleOutName, fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero))
using (SafeFileHandle stdin = NativeMethods.CreateFile(NativeMethods.ConsoleInName, fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero))
using (SafeFileHandle stdout = CreateFile(fileName: ConsoleOutName,
desiredAccess: fileAccessFlags,
shareMode: fileShareFlags,
securityAttributes: IntPtr.Zero,
creationDisposition: fileCreationDisposition,
flagsAndAttributes: fileAttributes,
templateFile: IntPtr.Zero))
using (SafeFileHandle stdin = CreateFile(fileName: ConsoleInName,
desiredAccess: fileAccessFlags,
shareMode: fileShareFlags,
securityAttributes: IntPtr.Zero,
creationDisposition: fileCreationDisposition,
flagsAndAttributes: fileAttributes,
templateFile: IntPtr.Zero))
{
// Read the current console mode.
@ -70,10 +82,11 @@ namespace Microsoft.Alm.Cli
program.Trace.WriteLine("not a tty detected, abandoning prompt.");
return null;
}
else if (!NativeMethods.GetConsoleMode(stdin, out consoleMode))
else if (!GetConsoleMode(consoleMode: out consoleMode,
consoleHandle: stdin))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to determine console mode (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to determine console mode (" + Win32Error.GetText(error) + ").");
}
program.Trace.WriteLine($"console mode = '{consoleMode}'.");
@ -85,10 +98,15 @@ namespace Microsoft.Alm.Cli
buffer.Append(titleMessage)
.Append(targetUri)
.AppendLine();
if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero))
if (!WriteConsole(buffer: buffer,
consoleOutputHandle: stdout,
numberOfCharsToWrite: (uint)buffer.Length,
numberOfCharsWritten: out written,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to write to standard output (" + Win32Error.GetText(error) + ").");
}
// Clear the buffer for the next operation.
@ -96,20 +114,28 @@ namespace Microsoft.Alm.Cli
// Prompt the user for the username wanted.
buffer.Append("username: ");
if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero))
if (!WriteConsole(buffer: buffer,
consoleOutputHandle: stdout,
numberOfCharsToWrite: (uint)buffer.Length,
numberOfCharsWritten: out written,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to write to standard output (" + Win32Error.GetText(error) + ").");
}
// Clear the buffer for the next operation.
buffer.Clear();
// Read input from the user.
if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero))
if (!ReadConsole(buffer: buffer,
consoleInputHandle: stdin,
numberOfCharsToRead: BufferReadSize,
numberOfCharsRead: out read,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to read from standard input (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to read from standard input (" + Win32Error.GetText(error) + ").");
}
// Record input from the user into local storage, stripping any EOL chars.
@ -120,34 +146,43 @@ namespace Microsoft.Alm.Cli
buffer.Clear();
// Set the console mode to current without echo input.
NativeMethods.ConsoleMode consoleMode2 = consoleMode ^ NativeMethods.ConsoleMode.EchoInput;
ConsoleMode consoleMode2 = consoleMode ^ ConsoleMode.EchoInput;
try
{
if (!NativeMethods.SetConsoleMode(stdin, consoleMode2))
if (!SetConsoleMode(consoleMode: consoleMode2,
consoleHandle: stdin))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to set console mode (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to set console mode (" + Win32Error.GetText(error) + ").");
}
program.Trace.WriteLine($"console mode = '{(consoleMode2 & NativeMethods.ConsoleMode.AllFlags)}'.");
program.Trace.WriteLine($"console mode = '{(consoleMode2 & ConsoleMode.AllFlags)}'.");
// Prompt the user for password.
buffer.Append("password: ");
if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero))
if (!WriteConsole(buffer: buffer,
consoleOutputHandle: stdout,
numberOfCharsToWrite: (uint)buffer.Length,
numberOfCharsWritten: out written,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to write to standard output (" + Win32Error.GetText(error) + ").");
}
// Clear the buffer for the next operation.
buffer.Clear();
// Read input from the user.
if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero))
if (!ReadConsole(buffer: buffer,
consoleInputHandle: stdin,
numberOfCharsToRead: BufferReadSize,
numberOfCharsRead: out read,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to read from standard input (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to read from standard input (" + Win32Error.GetText(error) + ").");
}
// Record input from the user into local storage, stripping any EOL chars.
@ -157,7 +192,8 @@ namespace Microsoft.Alm.Cli
finally
{
// Restore the console mode to its original value.
NativeMethods.SetConsoleMode(stdin, consoleMode);
SetConsoleMode(consoleMode: consoleMode,
consoleHandle: stdin);
program.Trace.WriteLine($"console mode = '{consoleMode}'.");
}
@ -219,11 +255,11 @@ namespace Microsoft.Alm.Cli
public static void SetStandardOutputWriter(Program program, TextWriter writer)
=> Console.SetOut(writer);
public static bool StandardHandleIsTty(Program program, NativeMethods.StandardHandleType handleType)
public static bool StandardHandleIsTty(Program program, StandardHandleType handleType)
{
var standardHandle = NativeMethods.GetStdHandle(handleType);
var handleFileType = NativeMethods.GetFileType(standardHandle);
return handleFileType == NativeMethods.FileType.Char;
var standardHandle = GetStdHandle(std: handleType);
var handleFileType = GetFileType(fileHandle: standardHandle);
return handleFileType == FileType.Char;
}
public static void Write(Program program, string message)

Просмотреть файл

@ -27,7 +27,7 @@ using System;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Alm.Authentication;
using static Microsoft.Alm.NativeMethods;
using Git = Microsoft.Alm.Authentication.Git;
namespace Microsoft.Alm.Cli
@ -35,14 +35,14 @@ namespace Microsoft.Alm.Cli
internal static class DialogFunctions
{
public static bool DisplayModal(Program program,
ref NativeMethods.CredentialUiInfo credUiInfo,
ref NativeMethods.CredentialPackFlags authPackage,
ref CredentialUiInfo credUiInfo,
ref CredentialPackFlags authPackage,
IntPtr packedAuthBufferPtr,
uint packedAuthBufferSize,
IntPtr inBufferPtr,
int inBufferSize,
bool saveCredentials,
NativeMethods.CredentialUiWindowsFlags flags,
CredentialUiWindowsFlags flags,
out string username,
out string password)
{
@ -54,17 +54,17 @@ namespace Microsoft.Alm.Cli
try
{
// open a standard Windows authentication dialog to acquire username + password credentials
if ((error = NativeMethods.CredUIPromptForWindowsCredentials(credInfo: ref credUiInfo,
authError: 0,
authPackage: ref authPackage,
inAuthBuffer: inBufferPtr,
inAuthBufferSize: (uint)inBufferSize,
outAuthBuffer: out packedAuthBufferPtr,
outAuthBufferSize: out packedAuthBufferSize,
saveCredentials: ref saveCredentials,
flags: flags)) != NativeMethods.Win32Error.Success)
if ((error = CredUIPromptForWindowsCredentials(credInfo: ref credUiInfo,
authError: 0,
authPackage: ref authPackage,
inAuthBuffer: inBufferPtr,
inAuthBufferSize: (uint)inBufferSize,
outAuthBuffer: out packedAuthBufferPtr,
outAuthBufferSize: out packedAuthBufferSize,
saveCredentials: ref saveCredentials,
flags: flags)) != Win32Error.Success)
{
program.Trace.WriteLine($"credential prompt failed ('{NativeMethods.Win32Error.GetText(error)}').");
program.Trace.WriteLine($"credential prompt failed ('{Win32Error.GetText(error)}').");
username = null;
password = null;
@ -81,21 +81,21 @@ namespace Microsoft.Alm.Cli
int domainLen = domainBuffer.Capacity;
// unpack the result into locally useful data
if (!NativeMethods.CredUnPackAuthenticationBuffer(flags: authPackage,
authBuffer: packedAuthBufferPtr,
authBufferSize: packedAuthBufferSize,
username: usernameBuffer,
maxUsernameLen: ref usernameLen,
domainName: domainBuffer,
maxDomainNameLen: ref domainLen,
password: passwordBuffer,
maxPasswordLen: ref passwordLen))
if (!CredUnPackAuthenticationBuffer(flags: authPackage,
authBuffer: packedAuthBufferPtr,
authBufferSize: packedAuthBufferSize,
username: usernameBuffer,
maxUsernameLen: ref usernameLen,
domainName: domainBuffer,
maxDomainNameLen: ref domainLen,
password: passwordBuffer,
maxPasswordLen: ref passwordLen))
{
username = null;
password = null;
error = Marshal.GetLastWin32Error();
program.Trace.WriteLine($"failed to unpack buffer ('{NativeMethods.Win32Error.GetText(error)}').");
program.Trace.WriteLine($"failed to unpack buffer ('{Win32Error.GetText(error)}').");
return false;
}
@ -125,16 +125,16 @@ namespace Microsoft.Alm.Cli
if (message is null)
throw new ArgumentNullException(nameof(message));
var credUiInfo = new NativeMethods.CredentialUiInfo
var credUiInfo = new CredentialUiInfo
{
BannerArt = IntPtr.Zero,
CaptionText = program.Title,
Parent = program.ParentHwnd,
MessageText = message,
Size = Marshal.SizeOf(typeof(NativeMethods.CredentialUiInfo))
Size = Marshal.SizeOf(typeof(CredentialUiInfo))
};
var flags = NativeMethods.CredentialUiWindowsFlags.Generic;
var authPackage = NativeMethods.CredentialPackFlags.None;
var flags = CredentialUiWindowsFlags.Generic;
var authPackage = CredentialPackFlags.None;
var packedAuthBufferPtr = IntPtr.Zero;
var inBufferPtr = IntPtr.Zero;
uint packedAuthBufferSize = 0;
@ -171,16 +171,16 @@ namespace Microsoft.Alm.Cli
if (username is null)
throw new ArgumentNullException(nameof(username));
var credUiInfo = new NativeMethods.CredentialUiInfo
var credUiInfo = new CredentialUiInfo
{
BannerArt = IntPtr.Zero,
CaptionText = program.Title,
MessageText = message,
Parent = program.ParentHwnd,
Size = Marshal.SizeOf(typeof(NativeMethods.CredentialUiInfo))
Size = Marshal.SizeOf(typeof(CredentialUiInfo))
};
var flags = NativeMethods.CredentialUiWindowsFlags.Generic;
var authPackage = NativeMethods.CredentialPackFlags.None;
var flags = CredentialUiWindowsFlags.Generic;
var authPackage = CredentialPackFlags.None;
var packedAuthBufferPtr = IntPtr.Zero;
var inBufferPtr = IntPtr.Zero;
uint packedAuthBufferSize = 0;
@ -194,29 +194,29 @@ namespace Microsoft.Alm.Cli
// Execute with `null` to determine buffer size always returns false when determining
// size, only fail if `inBufferSize` looks bad.
NativeMethods.CredPackAuthenticationBuffer(flags: authPackage,
username: username,
password: string.Empty,
packedCredentials: IntPtr.Zero,
packedCredentialsSize: ref inBufferSize);
CredPackAuthenticationBuffer(flags: authPackage,
username: username,
password: string.Empty,
packedCredentials: IntPtr.Zero,
packedCredentialsSize: ref inBufferSize);
if (inBufferSize <= 0)
{
error = Marshal.GetLastWin32Error();
program.Trace.WriteLine($"unable to determine credential buffer size ('{NativeMethods.Win32Error.GetText(error)}').");
program.Trace.WriteLine($"unable to determine credential buffer size ('{Win32Error.GetText(error)}').");
return null;
}
inBufferPtr = Marshal.AllocHGlobal(inBufferSize);
if (!NativeMethods.CredPackAuthenticationBuffer(flags: authPackage,
username: username,
password: string.Empty,
packedCredentials: inBufferPtr,
packedCredentialsSize: ref inBufferSize))
if (!CredPackAuthenticationBuffer(flags: authPackage,
username: username,
password: string.Empty,
packedCredentials: inBufferPtr,
packedCredentialsSize: ref inBufferSize))
{
error = Marshal.GetLastWin32Error();
program.Trace.WriteLine($"unable to write to credential buffer ('{NativeMethods.Win32Error.GetText(error)}').");
program.Trace.WriteLine($"unable to write to credential buffer ('{Win32Error.GetText(error)}').");
return null;
}

Просмотреть файл

@ -25,12 +25,11 @@
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Alm.Authentication;
using Microsoft.Win32.SafeHandles;
using Git = Microsoft.Alm.Authentication.Git;
using static Microsoft.Alm.NativeMethods;
using Github = GitHub.Authentication;
namespace Microsoft.Alm.Cli
@ -55,13 +54,27 @@ namespace Microsoft.Alm.Cli
authenticationCode = null;
NativeMethods.FileAccess fileAccessFlags = NativeMethods.FileAccess.GenericRead | NativeMethods.FileAccess.GenericWrite;
NativeMethods.FileAttributes fileAttributes = NativeMethods.FileAttributes.Normal;
NativeMethods.FileCreationDisposition fileCreationDisposition = NativeMethods.FileCreationDisposition.OpenExisting;
NativeMethods.FileShare fileShareFlags = NativeMethods.FileShare.Read | NativeMethods.FileShare.Write;
var fileAccessFlags = FileAccess.GenericRead
| FileAccess.GenericWrite;
var fileAttributes = FileAttributes.Normal;
var fileCreationDisposition = FileCreationDisposition.OpenExisting;
var fileShareFlags = FileShare.Read
| FileShare.Write;
using (SafeFileHandle stdout = NativeMethods.CreateFile(NativeMethods.ConsoleOutName, fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero))
using (SafeFileHandle stdin = NativeMethods.CreateFile(NativeMethods.ConsoleInName, fileAccessFlags, fileShareFlags, IntPtr.Zero, fileCreationDisposition, fileAttributes, IntPtr.Zero))
using (SafeFileHandle stdout = CreateFile(fileName: ConsoleOutName,
desiredAccess: fileAccessFlags,
shareMode: fileShareFlags,
securityAttributes: IntPtr.Zero,
creationDisposition: fileCreationDisposition,
flagsAndAttributes: fileAttributes,
templateFile: IntPtr.Zero))
using (SafeFileHandle stdin = CreateFile(fileName: ConsoleInName,
desiredAccess: fileAccessFlags,
shareMode: fileShareFlags,
securityAttributes: IntPtr.Zero,
creationDisposition: fileCreationDisposition,
flagsAndAttributes: fileAttributes,
templateFile: IntPtr.Zero))
{
string type = resultType == Github.GitHubAuthenticationResultType.TwoFactorApp
? "app"
@ -74,7 +87,11 @@ namespace Microsoft.Alm.Cli
.Append(type)
.Append("): ");
if (!NativeMethods.WriteConsole(stdout, buffer, (uint)buffer.Length, out written, IntPtr.Zero))
if (!WriteConsole(buffer: buffer,
consoleOutputHandle: stdout,
numberOfCharsToWrite: (uint)buffer.Length,
numberOfCharsWritten: out written,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to write to standard output (" + NativeMethods.Win32Error.GetText(error) + ").");
@ -82,10 +99,14 @@ namespace Microsoft.Alm.Cli
buffer.Clear();
// read input from the user
if (!NativeMethods.ReadConsole(stdin, buffer, BufferReadSize, out read, IntPtr.Zero))
if (!ReadConsole(buffer: buffer,
consoleInputHandle: stdin,
numberOfCharsToRead: BufferReadSize,
numberOfCharsRead: out read,
reserved: IntPtr.Zero))
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error, "Unable to read from standard input (" + NativeMethods.Win32Error.GetText(error) + ").");
throw new Win32Exception(error, "Unable to read from standard input (" + Win32Error.GetText(error) + ").");
}
authenticationCode = buffer.ToString(0, (int)read);

Просмотреть файл

@ -50,36 +50,42 @@ namespace Microsoft.Alm
/// If the <see cref="LineInput"/> mode is also enabled, backspace, carriage return, and line feed characters are handled by the system.
/// </summary>
ProcessedInput = 0x0001,
/// <summary>
/// The `<see cref="ReadConsole(SafeFileHandle, StringBuilder, uint, out uint, IntPtr)"/>` function returns only when a carriage return character is read.
/// <para/>
/// If this mode is disabled, the functions return when one or more characters are available.
/// </summary>
LineInput = 0x0002,
/// <summary>
/// Characters read by the `<see cref="ReadConsole(SafeFileHandle, StringBuilder, uint, out uint, IntPtr)"/>` function are written to the active screen buffer as they are read.
/// <para/>
/// This mode can be used only if the <see cref="LineInput"/> mode is also enabled.
/// </summary>
EchoInput = 0x0004,
/// <summary>
/// User interactions that change the size of the console screen buffer are reported in the console's input buffer.
/// <para/>
/// Information about these events can be read from the input buffer by applications using the ReadConsoleInput function, but not by those using `<see cref="ReadConsole(SafeFileHandle, StringBuilder, uint, out uint, IntPtr)"/>`.
/// </summary>
WindowInput = 0x0008,
/// <summary>
/// If the mouse pointer is within the borders of the console window and the window has the keyboard focus, mouse events generated by mouse movement and button presses are placed in the input buffer.
/// <para/>
/// These events are discarded by `<see cref="ReadConsole(SafeFileHandle, StringBuilder, uint, out uint, IntPtr)"/>`, even when this mode is enabled.
/// </summary>
MouseInput = 0x0010,
/// <summary>
/// When enabled, text entered in a console window will be inserted at the current cursor location and all text following that location will not be overwritten.
/// <para/>
/// When disabled, all following text will be overwritten.
/// </summary>
InsertMode = 0x0020,
/// <summary>
/// This flag enables the user to use the mouse to select and edit text.
/// </summary>
@ -91,6 +97,7 @@ namespace Microsoft.Alm
/// Backspace, tab, bell, carriage return, and line feed characters are processed.
/// </summary>
ProcessedOuput = 0x0001,
/// <summary>
/// When writing with `<see cref="WriteConsole(SafeHandle, StringBuilder, uint, out uint, IntPtr)"/>` or echoing with ReadFile or ReadConsole, the cursor moves to the beginning of the next row when it reaches the end of the current row.
/// <para/>
@ -117,8 +124,11 @@ namespace Microsoft.Alm
public enum FileAccess : uint
{
GenericRead = 0x80000000,
GenericWrite = 0x40000000,
GenericExecute = 0x20000000,
GenericAll = 0x10000000,
}
@ -131,15 +141,19 @@ namespace Microsoft.Alm
/// Applications can read the file, but cannot write to or delete it.
/// </summary>
Readonly = 0x00000001,
/// <summary>
/// The file is hidden. Do not include it in an ordinary directory listing.
/// </summary>
Hidden = 0x00000002,
/// <summary>
/// The file is part of or used exclusively by an operating system.
/// </summary>
System = 0x00000004,
Directory = 0x00000010,
/// <summary>
/// The file should be archived.
/// <para/>
@ -147,20 +161,27 @@ namespace Microsoft.Alm
/// or removal.
/// </summary>
Archive = 0x00000020,
Device = 0x00000040,
/// <summary>
/// The file does not have other attributes set.
/// <para/>
/// This attribute is valid only if used alone.
/// </summary>
Normal = 0x00000080,
/// <summary>
/// The file is being used for temporary storage.
/// </summary>
Temporary = 0x00000100,
SparseFile = 0x00000200,
ReparsePoint = 0x00000400,
Compressed = 0x00000800,
/// <summary>
/// The data of a file is not immediately available. This attribute indicates that file data is physically moved to offline storage.
/// <para/>
@ -169,7 +190,9 @@ namespace Microsoft.Alm
/// Applications should not arbitrarily change this attribute.
/// </summary>
Offline = 0x00001000,
NotContentIndexed = 0x00002000,
/// <summary>
/// The file or directory is encrypted.
/// <para/>For a file, this means that all data in the file is encrypted.
@ -181,7 +204,9 @@ namespace Microsoft.Alm
/// This flag is not supported on Home, Home Premium, Starter, or ARM editions of Windows.
/// </summary>
Encrypted = 0x00004000,
FirstPipeInstance = 0x00080000,
/// <summary>
/// The file data is requested, but it should continue to be located in remote storage.
/// <para/>
@ -190,6 +215,7 @@ namespace Microsoft.Alm
/// This flag is for use by remote storage systems.
/// </summary>
OpenNoRecall = 0x00100000,
/// <summary>
/// Normal reparse point processing will not occur; `<see cref="CreateFile(string, FileAccess, FileShare, IntPtr, FileCreationDisposition, FileAttributes, IntPtr)"/>` will attempt to open the reparse point. When a file is opened, a file handle is returned, whether or not the filter that controls the reparse point is operational.
/// <para/>
@ -198,6 +224,7 @@ namespace Microsoft.Alm
/// If the file is not a reparse point, then this flag is ignored.
/// </summary>
OpenReparsePoint = 0x00200000,
/// <summary>
/// The file or device is being opened with session awareness.
/// <para/>
@ -208,6 +235,7 @@ namespace Microsoft.Alm
/// This flag is supported only on server editions of Windows.
/// </summary>
SessionAware = 0x00800000,
/// <summary>
/// Access will occur according to POSIX rules.
/// <para/>
@ -216,6 +244,7 @@ namespace Microsoft.Alm
/// Use care when using this option, because files created with this flag may not be accessible by applications that are written for MS-DOS or 16-bit Windows.
/// </summary>
PosixSemantics = 0x01000000,
/// <summary>
/// The file is being opened or created for a backup or restore operation.
/// <para/>
@ -226,6 +255,7 @@ namespace Microsoft.Alm
/// A directory handle can be passed to some functions instead of a file handle.
/// </summary>
BackupSemantics = 0x02000000,
/// <summary>
/// The file is to be deleted immediately after all of its handles are closed, which includes the specified handle and any other open or duplicated handles.
/// <para/>
@ -234,6 +264,7 @@ namespace Microsoft.Alm
/// Subsequent open requests for the file fail, unless the `<see cref="FileShare.Delete"/>` share mode is specified.
/// </summary>
DeleteOnClose = 0x04000000,
/// <summary>
/// Access is intended to be sequential from beginning to end. The system can use this as a hint to optimize file caching.
/// <para/>
@ -242,12 +273,14 @@ namespace Microsoft.Alm
/// This flag has no effect if the file system does not support cached I/O and `<see cref="NoBuffering"/>`.
/// </summary>
SequentialScan = 0x08000000,
/// <summary>
/// Access is intended to be random. The system can use this as a hint to optimize file caching.
/// <para/>
/// This flag has no effect if the file system does not support cached I/O and `<see cref="NoBuffering"/>`.
/// </summary>
RandomAccess = 0x10000000,
/// <summary>
/// The file or device is being opened with no system caching for data reads and writes.
/// <para/>
@ -256,6 +289,7 @@ namespace Microsoft.Alm
/// There are strict requirements for successfully working with files opened with `<see cref="CreateFile(string, FileAccess, FileShare, IntPtr, FileCreationDisposition, FileAttributes, IntPtr)"/>` using the `<see cref="NoBuffering"/>` flag.
/// </summary>
NoBuffering = 0x20000000,
/// <summary>
/// The file or device is being opened or created for asynchronous I/O.
/// <para/>
@ -266,6 +300,7 @@ namespace Microsoft.Alm
/// If this flag is not specified, then I/O operations are serialized, even if the calls to the read and write functions specify an OVERLAPPED structure.
/// </summary>
Overlapped = 0x40000000,
/// <summary>
/// Write operations will not go through any intermediate cache, they will go directly to disk.
/// </summary>
@ -282,6 +317,7 @@ namespace Microsoft.Alm
/// If the specified file does not exist and is a valid path to a writable location, a new file is created.
/// </summary>
New = 1,
/// <summary>
/// Creates a new file, always.
/// <para/>
@ -290,6 +326,7 @@ namespace Microsoft.Alm
/// If the specified file does not exist and is a valid path, a new file is created, the function succeeds, and the last-error code is set to zero.
/// </summary>
CreateAlways = 2,
/// <summary>
/// Opens a file, always.
/// <para/>
@ -298,12 +335,14 @@ namespace Microsoft.Alm
/// If the specified file does not exist and is a valid path to a writable location, the function creates a file and the last-error code is set to zero.
/// </summary>
OpenExisting = 3,
/// <summary>
/// Opens a file or device, only if it exists.
/// <para/>
/// If the specified file or device does not exist, the function fails and the last-error code is set to <see cref="Win32Error.FileNotFound"/>.
/// </summary>
OpenAlways = 4,
/// <summary>
/// Opens a file and truncates it so that its size is zero bytes, only if it exists.
/// <para/>
@ -322,6 +361,7 @@ namespace Microsoft.Alm
/// Prevents other processes from opening a file or device if they request delete, read, or write access.
/// </summary>
None = 0x00000000,
/// <summary>
/// Enables subsequent open operations on an object to request read access.
/// <para/>
@ -330,6 +370,7 @@ namespace Microsoft.Alm
/// If this flag is not specified, but the object has been opened for read access, the function fails.
/// </summary>
Read = 0x00000001,
/// <summary>
/// Enables subsequent open operations on an object to request write access.
/// <para/>
@ -339,6 +380,7 @@ namespace Microsoft.Alm
/// function fails.
/// </summary>
Write = 0x00000002,
/// <summary>
/// Enables subsequent open operations on an object to request delete access.
/// <para/>
@ -355,18 +397,22 @@ namespace Microsoft.Alm
/// Either the type of the specified file is unknown, or the function failed.
/// </summary>
Unknown = 0x0000,
/// <summary>
/// The specified file is a disk file.
/// </summary>
Disk = 0x0001,
/// <summary>
/// The specified file is a character file, typically an LPT device or a console.
/// </summary>
Char = 0x0002,
/// <summary>
/// The specified file is a socket, a named pipe, or an anonymous pipe.
/// </summary>
Pipe = 0x0003,
/// <summary>
/// Unused.
/// </summary>
@ -379,10 +425,12 @@ namespace Microsoft.Alm
/// The standard input device. Initially, this is the console input buffer, CONIN$.
/// </summary>
Input = -10,
/// <summary>
/// The standard output device. Initially, this is the active console screen buffer, CONOUT$.
/// </summary>
Output = -11,
/// <summary>
/// The standard error device. Initially, this is the active console screen buffer, CONOUT$.
/// </summary>
@ -393,8 +441,11 @@ namespace Microsoft.Alm
public enum CredentialPackFlags : uint
{
None = 0,
ProtectedCredentials = 0x1,
WowBuffer = 0x2,
GenericCredentials = 0x4,
}
@ -408,18 +459,21 @@ namespace Microsoft.Alm
/// The credential will be stored securely but has no other significant characteristics.
/// </summary>
Generic = 0x01,
/// <summary>
/// The ` <see cref="Credential"/>` is a password credential and is specific to Microsoft's authentication packages.
/// <para/>
/// The NTLM, Kerberos, and Negotiate authentication packages will automatically use this credential when connecting to the named target.
/// </summary>
DomainPassword = 0x02,
/// <summary>
/// The ` <see cref="Credential"/>` is a certificate credential and is specific to Microsoft's authentication packages.
/// <para/>
/// The Kerberos, Negotiate, and SChannel authentication packages automatically use this credential when connecting to the named target.
/// </summary>
DomainCertificate = 0x03,
/// <summary>
/// The ` <see cref="Credential"/>` is a password credential and is specific to authentication packages from Microsoft.
/// <para/>
@ -427,10 +481,12 @@ namespace Microsoft.Alm
/// </summary>
[Obsolete("This value is no longer supported", true)]
DomainVisiblePassword = 0x04,
/// <summary>
/// The ` <see cref="Credential"/>` is a certificate credential that is a generic authentication package.
/// </summary>
GenericCertificate = 0x05,
/// <summary>
/// The ` <see cref="Credential"/>` is supported by extended Negotiate packages.
/// </summary>
@ -438,11 +494,13 @@ namespace Microsoft.Alm
/// Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not supported.
/// </remarks>
DomainExtended = 0x06,
/// <summary>
/// The maximum number of supported credential types.
/// </summary>
/// <remarks> Windows Server 2008, Windows Vista, Windows Server 2003, and Windows XP: This value is not supported. </remarks>
Maximum = 0x07,
/// <summary>
/// The extended maximum number of supported credential types that now allow new applications to run on older operating systems.
/// </summary>
@ -456,34 +514,58 @@ namespace Microsoft.Alm
public enum CredentialUiFlags : uint
{
None = 0,
IncorrectPassword = 0x1,
DoNoPersist = 0x2,
EquestAdministrator = 0x4,
ExcludeCertificates = 0x8,
RequireCertificates = 0x10,
ShowSaveCheckbox = 0x40,
AlwaysShowUi = 0x80,
RequireSmartCard = 0x100,
PasswordOnlyOk = 0x200,
ValidateUsername = 0x400,
CompleteUsername = 0x800,
Persist = 0x1000,
ServerCredential = 0x4000,
ExpectConfermatino = 0x20000,
GenericCredentals = 0x40000,
UsernameTargetCredentials = 0x80000,
KeepUsername = 0x100000,
}
public enum CredentialUiResult : uint
{
Success = 0,
Cancelled = 1223,
NoSuchLogonSession = 1312,
NotFound = 1168,
InvalidAccountName = 1315,
InsufficientBuffer = 122,
InvalidParameter = 87,
InvalidFlags = 1004,
}
@ -491,24 +573,29 @@ namespace Microsoft.Alm
public enum CredentialUiWindowsFlags : uint
{
None = 0,
/// <summary>
/// The caller is requesting that the credential provider return the user name and password in plain text.
/// </summary>
Generic = 0x1,
Generic = 0x0001,
/// <summary>
/// The Save check box is displayed in the dialog box.
/// </summary>
Checkbox = 0x2,
Checkbox = 0x0002,
/// <summary>
/// Only credential providers that support the authentication package specified by the `authPackage` parameter should be enumerated.
/// </summary>
AuthPackageOnly = 0x10,
AuthPackageOnly = 0x0010,
/// <summary>
/// Only the credentials specified by the `inAuthBuffer` parameter for the authentication package specified by the `authPackage` parameter should be enumerated.
/// <para/>
/// If this flag is set, and the `inAuthBuffer` parameter is `null`, the function fails.
/// </summary>
InCredOnly = 0x20,
InCredOnly = 0x0020,
/// <summary>
/// Credential providers should enumerate only administrators.
/// <para/>
@ -516,23 +603,27 @@ namespace Microsoft.Alm
/// <para/>
/// We recommend that external callers not set this flag.
/// </summary>
EnumerateAdmins = 0x100,
EnumerateAdmins = 0x0100,
/// <summary>
/// Only the incoming credentials for the authentication package specified by the `authPackage` parameter should be enumerated.
/// </summary>
EnumerateCurrentUser = 0x200,
EnumerateCurrentUser = 0x0200,
/// <summary>
/// The credential dialog box should be displayed on the secure desktop.
/// <para/>
/// This value cannot be combined with <see cref="Generic"/>.
/// </summary>
SecurePrompt = 0x1000,
/// <summary>
/// The credential dialog box is invoked by the SspiPromptForCredentials function, and the client is prompted before a prior handshake.
/// <para/>
/// If SSPIPFC_NO_CHECKBOX is passed in the `inAuthBuffer` parameter, then the credential provider should not display the check box.
/// </summary>
Preprompting = 0x2000,
/// <summary>
/// The credential provider should align the credential BLOB pointed to by the `outAuthBuffer` parameter to a 32-bit boundary, even if the provider is running on a 64-bit system.
/// </summary>
@ -544,12 +635,16 @@ namespace Microsoft.Alm
{
[MarshalAs(UnmanagedType.U4)]
public int Size;
[MarshalAs(UnmanagedType.SysInt)]
public IntPtr Parent;
[MarshalAs(UnmanagedType.LPWStr)]
public string MessageText;
[MarshalAs(UnmanagedType.LPWStr)]
public string CaptionText;
[MarshalAs(UnmanagedType.SysInt)]
public IntPtr BannerArt;
}

Просмотреть файл

@ -35,6 +35,7 @@ using Vsts = VisualStudioTeamServices.Authentication;
namespace Microsoft.Alm.Cli
{
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay, nq}")]
internal class OperationArguments : Base
{
/// <summary>
@ -444,6 +445,11 @@ namespace Microsoft.Alm.Cli
set { _writeLog = value; }
}
internal string DebuggerDisplay
{
get { return $"{nameof(OperationArguments)}: TargetUri: {TargetUri}, Authority: {Authority}, Credentials: {Credentials}"; }
}
/// <summary>
/// Loads the Git configuration based on `<seealso cref="Environment.CurrentDirectory"/>`.
/// </summary>

Просмотреть файл

@ -600,16 +600,17 @@ namespace Microsoft.Alm.Cli
_version = asseName.Version;
}
internal bool ModalPromptDisplayDialog(ref NativeMethods.CredentialUiInfo credUiInfo,
ref NativeMethods.CredentialPackFlags authPackage,
IntPtr packedAuthBufferPtr,
uint packedAuthBufferSize,
IntPtr inBufferPtr,
int inBufferSize,
bool saveCredentials,
NativeMethods.CredentialUiWindowsFlags flags,
out string username,
out string password)
internal bool ModalPromptDisplayDialog(
ref NativeMethods.CredentialUiInfo credUiInfo,
ref NativeMethods.CredentialPackFlags authPackage,
IntPtr packedAuthBufferPtr,
uint packedAuthBufferSize,
IntPtr inBufferPtr,
int inBufferSize,
bool saveCredentials,
NativeMethods.CredentialUiWindowsFlags flags,
out string username,
out string password)
=> _modalPromptDisplayDialog(this,
ref credUiInfo,
ref authPackage,