1
0
Форкнуть 0

cli: add GCM_OVERRIDE_URL support.

Provide a mechanism for setting `TargetUri.ActualUri` via environment variables.

 - [x] Add `UrlOverride` property to `OperationArguments`.
 - [x] Add `UrlOverride` to the `KeyType` enumeration.
 - [x] Modify `OperationArguments` to use `UrlOverride` in its calculation of `TargetUri`.
 - [x] Add feature validation test.
This commit is contained in:
J Wyman 2018-07-19 00:14:33 -04:00 коммит произвёл J Wyman ∞
Родитель 8f4673bad6
Коммит a8d87f349c
7 изменённых файлов: 4013 добавлений и 274 удалений

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

@ -123,7 +123,7 @@ namespace Microsoft.Alm.Cli
{
_context.Trace.WriteLine($"converted '{url}' to '{uri.AbsoluteUri}'.");
OperationArguments operationArguments = new OperationArguments(_context);
var operationArguments = new OperationArguments(_context);
operationArguments.SetTargetUri(uri);
@ -357,8 +357,12 @@ namespace Microsoft.Alm.Cli
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process.
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;
@ -381,7 +385,7 @@ namespace Microsoft.Alm.Cli
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
{
OperationArguments operationArguments = new OperationArguments(_context);
var operationArguments = new OperationArguments(_context);
Task.Run(async () =>
{
@ -397,8 +401,12 @@ namespace Microsoft.Alm.Cli
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process.
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;
@ -433,7 +441,7 @@ namespace Microsoft.Alm.Cli
{
string doc = Path.Combine(installation.Doc, HelpFileName);
// if the help file exists, send it to the operating system to display to the user
// If the help file exists, send it to the operating system to display to the user.
if (Storage.FileExists(doc))
{
Trace.WriteLine($"opening help documentation '{doc}'.");
@ -467,7 +475,7 @@ namespace Microsoft.Alm.Cli
// see: https://www.kernel.org/pub/software/scm/git/docs/git-credential.html
using (var stdin = InStream)
{
OperationArguments operationArguments = new OperationArguments(_context);
var operationArguments = new OperationArguments(_context);
Task.Run(async () =>
{
@ -488,8 +496,12 @@ namespace Microsoft.Alm.Cli
await LoadOperationArguments(operationArguments);
EnableTraceLogging(operationArguments);
// Read the details of any git-remote-http(s).exe parent process.
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;

122
Cli/Test/BasicLogonTests.cs Normal file
Просмотреть файл

@ -0,0 +1,122 @@
using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Alm.Authentication;
using Xunit;
using static System.StringComparer;
namespace Microsoft.Alm.Cli.Test
{
public class BasicLogonTests : Authentication.Test.UnitTestBase
{
private static readonly Encoding Utf8 = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
public BasicLogonTests(Xunit.Abstractions.ITestOutputHelper output)
: base(XunitHelper.Convert(output))
{ }
[Fact]
public void EnvironmentUrlOverride()
{
const string protocol = "https";
const string host = "microsoft-git-tools.visualstudio.com";
const string urlOverride = protocol + "://github.com";
Environment.SetEnvironmentVariable("GCM_URL_OVERRIDE", urlOverride, EnvironmentVariableTarget.Process);
InitializeTest();
var errorBuffer = new byte[4096];
var outputBuffer = new byte[4096];
var program = new Program(Context);
using (var inputStream = new MemoryStream())
using (var outputStream = new MemoryStream(outputBuffer))
using (var errorStream = new MemoryStream(errorBuffer))
using (var writer = new StreamWriter(inputStream, Utf8))
{
SetupProgramStandardPipes(program, inputStream, outputStream, errorStream);
MimicGitCredential(writer, protocol, host);
inputStream.Seek(0, SeekOrigin.Begin);
program._exit = (Program p, int exitcode, string message, string path, int line, string name) =>
{
Assert.Same(program, p);
Assert.Equal(-1, exitcode);
Assert.Equal(Program.LogonFailedMessage, message, Ordinal);
};
program._queryCredentials = (Program p, OperationArguments opArgs) =>
{
Assert.Same(program, p);
Assert.NotNull(opArgs);
Assert.NotNull(opArgs.UrlOverride);
Assert.NotNull(opArgs.TargetUri);
Assert.Equal(urlOverride, opArgs.UrlOverride, OrdinalIgnoreCase);
var actualUrl = opArgs.TargetUri.ActualUri?.ToString()?.TrimEnd('/');
var queryUrl = opArgs.TargetUri.QueryUri.ToString().TrimEnd('/');
Assert.Equal(urlOverride, actualUrl, OrdinalIgnoreCase);
Assert.Equal(protocol + "://" + host, queryUrl, OrdinalIgnoreCase);
return Task.FromResult<Credential>(null);
};
program.Get();
}
}
private static void MimicGitCredential(TextWriter writer, string protocol, string host)
{
writer.Write("protocol=");
writer.Write(protocol);
writer.Write("\n");
writer.Write("host=");
writer.Write(host);
writer.Write("\n");
writer.Write("\n");
writer.Flush();
}
private static void SetupProgramStandardPipes(Program program, Stream standardInput, Stream standardOutput, Stream standardError)
{
program._openStandardErrorStream = (Program p) =>
{
Assert.Same(program, p);
return standardError;
};
program._openStandardInputStream = (Program p) =>
{
Assert.Same(program, p);
return standardInput;
};
program._openStandardOutputStream = (Program p) =>
{
Assert.Same(program, p);
return standardOutput;
};
program._write = (Program p, string message) =>
{
Assert.Same(program, p);
var buffer = Encoding.Unicode.GetBytes(message);
standardError.Write(buffer, 0, buffer.Length);
};
program._writeLine = (Program p, string message) =>
{
Assert.Same(program, p);
var buffer = Encoding.Unicode.GetBytes(message + Environment.NewLine);
standardError.Write(buffer, 0, buffer.Length);
};
}
}
}

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

@ -72,6 +72,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="BasicAuthenticationTests.cs" />
<Compile Include="BasicLogonTests.cs" />
<Compile Include="BitbucketLogonTests.cs" />
<Compile Include="GlobalSuppressions.cs" />
<Compile Include="OperationArgumentsTests.cs" />

Разница между файлами не показана из-за своего большого размера Загрузить разницу

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

@ -82,125 +82,125 @@ namespace Microsoft.Alm.Cli
switch (operationArguments.Authority)
{
case AuthorityType.Auto:
program.Trace.WriteLine($"detecting authority type for '{operationArguments.TargetUri}'.");
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,
// Detect the authority.
authority = await Vsts.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);
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)
if (authority != null)
{
// Set the authority type based on the returned value.
if (authority is Vsts.MsaAuthentication)
{
// 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;
}
operationArguments.Authority = AuthorityType.MicrosoftAccount;
goto case AuthorityType.MicrosoftAccount;
}
goto default;
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.");
program.Trace.WriteLine($"authority for '{operationArguments.TargetUri}' is Azure Directory.");
if (authority is null)
if (authority is null)
{
Guid tenantId = Guid.Empty;
// Get the identity of the tenant.
var result = await Vsts.Authentication.DetectAuthority(program.Context, operationArguments.TargetUri);
if (result.HasValue)
{
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));
tenantId = result.Value;
}
// Return the allocated authority or a generic AAD backed VSTS authentication object.
return authority;
// 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;
case AuthorityType.Basic:
// Enforce basic authentication only.
basicNtlmSupport = NtlmSupport.Never;
goto default;
// 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;
goto default;
// 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 +217,24 @@ 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);
}
}
@ -617,6 +617,17 @@ namespace Microsoft.Alm.Cli
Trace.WriteLine($"Failed to parse {program.KeyTypeName(KeyType.ParentHwnd)}.");
}
}
// Check for URL overrides provided by the calling process.
if (program.TryReadString(operationArguments, KeyType.UrlOverride, out value))
{
program.Trace.WriteLine($"{program.KeyTypeName(KeyType.UrlOverride)} = '{value}'.");
if (Uri.TryCreate(value, UriKind.Absolute, out Uri actualUri))
{
operationArguments.UrlOverride = value;
}
}
}
public static void LogEvent(Program program, string message, EventLogEntryType eventType)
@ -688,154 +699,154 @@ namespace Microsoft.Alm.Cli
{
default:
case AuthorityType.Basic:
{
var basicAuth = authentication as BasicAuthentication;
{
var basicAuth = authentication as BasicAuthentication;
// Attempt to get cached credentials or acquire credentials if interactivity is allowed.
if ((operationArguments.Interactivity != Interactivity.Always
&& (credentials = await authentication.GetCredentials(operationArguments.TargetUri)) != null)
|| (operationArguments.Interactivity != Interactivity.Never
&& (credentials = await basicAuth.AcquireCredentials(operationArguments.TargetUri)) != null))
{
program.Trace.WriteLine("credentials found.");
// No need to save the credentials explicitly, as Git will call back
// with a store command if the credentials are valid.
}
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
// Attempt to get cached credentials or acquire credentials if interactivity is allowed.
if ((operationArguments.Interactivity != Interactivity.Always
&& (credentials = await authentication.GetCredentials(operationArguments.TargetUri)) != null)
|| (operationArguments.Interactivity != Interactivity.Never
&& (credentials = await basicAuth.AcquireCredentials(operationArguments.TargetUri)) != null))
{
program.Trace.WriteLine("credentials found.");
// No need to save the credentials explicitly, as Git will call back
// with a store command if the credentials are valid.
}
break;
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
}
break;
case AuthorityType.AzureDirectory:
{
var aadAuth = authentication as Vsts.AadAuthentication;
var patOptions = new Vsts.PersonalAccessTokenOptions()
{
var aadAuth = authentication as Vsts.AadAuthentication;
var patOptions = new Vsts.PersonalAccessTokenOptions()
{
RequireCompactToken = true,
TokenDuration = operationArguments.TokenDuration,
TokenScope = null,
};
RequireCompactToken = true,
TokenDuration = operationArguments.TokenDuration,
TokenScope = null,
};
// Attempt to get cached credentials -> non-interactive logon -> interactive
// logon note that AAD "credentials" are always scoped access tokens.
if (((operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await aadAuth.GetCredentials(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await aadAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
|| (operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await aadAuth.NoninteractiveLogon(operationArguments.TargetUri, patOptions)) != null)
&& (!operationArguments.ValidateCredentials
|| await aadAuth.ValidateCredentials(operationArguments.TargetUri, credentials)))
|| (operationArguments.Interactivity != Interactivity.Never
&& ((credentials = await aadAuth.InteractiveLogon(operationArguments.TargetUri, patOptions)) != null)
&& (!operationArguments.ValidateCredentials
|| await aadAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
program.LogEvent($"Azure Directory credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve Azure Directory credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
// Attempt to get cached credentials -> non-interactive logon -> interactive
// logon note that AAD "credentials" are always scoped access tokens.
if (((operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await aadAuth.GetCredentials(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await aadAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
|| (operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await aadAuth.NoninteractiveLogon(operationArguments.TargetUri, patOptions)) != null)
&& (!operationArguments.ValidateCredentials
|| await aadAuth.ValidateCredentials(operationArguments.TargetUri, credentials)))
|| (operationArguments.Interactivity != Interactivity.Never
&& ((credentials = await aadAuth.InteractiveLogon(operationArguments.TargetUri, patOptions)) != null)
&& (!operationArguments.ValidateCredentials
|| await aadAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
program.LogEvent($"Azure Directory credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
break;
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve Azure Directory credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
}
break;
case AuthorityType.MicrosoftAccount:
{
var msaAuth = authentication as Vsts.MsaAuthentication;
var patOptions = new Vsts.PersonalAccessTokenOptions()
{
var msaAuth = authentication as Vsts.MsaAuthentication;
var patOptions = new Vsts.PersonalAccessTokenOptions()
{
RequireCompactToken = true,
TokenDuration = operationArguments.TokenDuration,
TokenScope = null,
};
RequireCompactToken = true,
TokenDuration = operationArguments.TokenDuration,
TokenScope = null,
};
// Attempt to get cached credentials -> interactive logon note that MSA
// "credentials" are always scoped access tokens.
if (((operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await msaAuth.GetCredentials(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await msaAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
|| (operationArguments.Interactivity != Interactivity.Never
&& ((credentials = await msaAuth.InteractiveLogon(operationArguments.TargetUri, patOptions)) != null)
&& (!operationArguments.ValidateCredentials
|| await msaAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
program.LogEvent($"Microsoft Live credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve Microsoft Live credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
// Attempt to get cached credentials -> interactive logon note that MSA
// "credentials" are always scoped access tokens.
if (((operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await msaAuth.GetCredentials(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await msaAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
|| (operationArguments.Interactivity != Interactivity.Never
&& ((credentials = await msaAuth.InteractiveLogon(operationArguments.TargetUri, patOptions)) != null)
&& (!operationArguments.ValidateCredentials
|| await msaAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
program.LogEvent($"Microsoft Live credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
break;
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve Microsoft Live credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
}
break;
case AuthorityType.GitHub:
{
var ghAuth = authentication as Github.Authentication;
{
var ghAuth = authentication as Github.Authentication;
if ((operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await ghAuth.GetCredentials(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await ghAuth.ValidateCredentials(operationArguments.TargetUri, credentials)))
|| (operationArguments.Interactivity != Interactivity.Never
&& ((credentials = await ghAuth.InteractiveLogon(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await ghAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
program.LogEvent($"GitHub credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve GitHub credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
if ((operationArguments.Interactivity != Interactivity.Always
&& ((credentials = await ghAuth.GetCredentials(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await ghAuth.ValidateCredentials(operationArguments.TargetUri, credentials)))
|| (operationArguments.Interactivity != Interactivity.Never
&& ((credentials = await ghAuth.InteractiveLogon(operationArguments.TargetUri)) != null)
&& (!operationArguments.ValidateCredentials
|| await ghAuth.ValidateCredentials(operationArguments.TargetUri, credentials))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
program.LogEvent($"GitHub credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
break;
else
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' not found.");
program.LogEvent($"Failed to retrieve GitHub credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
}
break;
case AuthorityType.Bitbucket:
{
var bbcAuth = authentication as Bitbucket.Authentication;
{
var bbcAuth = authentication as Bitbucket.Authentication;
if (((operationArguments.Interactivity != Interactivity.Always)
&& ((credentials = await bbcAuth.GetCredentials(operationArguments.TargetUri, operationArguments.Username)) != null)
&& (!operationArguments.ValidateCredentials
|| ((credentials = await bbcAuth.ValidateCredentials(operationArguments.TargetUri, operationArguments.Username, credentials)) != null)))
|| ((operationArguments.Interactivity != Interactivity.Never)
&& ((credentials = await bbcAuth.InteractiveLogon(operationArguments.TargetUri, operationArguments.Username)) != null)
&& (!operationArguments.ValidateCredentials
|| ((credentials = await bbcAuth.ValidateCredentials(operationArguments.TargetUri, operationArguments.Username, credentials)) != null))))
if (((operationArguments.Interactivity != Interactivity.Always)
&& ((credentials = await bbcAuth.GetCredentials(operationArguments.TargetUri, operationArguments.Username)) != null)
&& (!operationArguments.ValidateCredentials
|| ((credentials = await bbcAuth.ValidateCredentials(operationArguments.TargetUri, operationArguments.Username, credentials)) != null)))
|| ((operationArguments.Interactivity != Interactivity.Never)
&& ((credentials = await bbcAuth.InteractiveLogon(operationArguments.TargetUri, operationArguments.Username)) != null)
&& (!operationArguments.ValidateCredentials
|| ((credentials = await bbcAuth.ValidateCredentials(operationArguments.TargetUri, operationArguments.Username, credentials)) != null))))
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
// Bitbucket relies on a username + secret, so make sure there is a
// username to return.
if (operationArguments.Username != null)
{
program.Trace.WriteLine($"credentials for '{operationArguments.TargetUri}' found.");
// Bitbucket relies on a username + secret, so make sure there is a
// username to return.
if (operationArguments.Username != null)
{
credentials = new Credential(operationArguments.Username, credentials.Password);
}
program.LogEvent($"Bitbucket credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
else
{
program.LogEvent($"Failed to retrieve Bitbucket credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
credentials = new Credential(operationArguments.Username, credentials.Password);
}
program.LogEvent($"Bitbucket credentials for '{operationArguments.TargetUri}' successfully retrieved.", EventLogEntryType.SuccessAudit);
}
break;
else
{
program.LogEvent($"Failed to retrieve Bitbucket credentials for '{operationArguments.TargetUri}'.", EventLogEntryType.FailureAudit);
}
}
break;
case AuthorityType.Ntlm:
{
program.Trace.WriteLine($"'{operationArguments.TargetUri}' is NTLM.");
credentials = BasicAuthentication.NtlmCredentials;
}
break;
{
program.Trace.WriteLine($"'{operationArguments.TargetUri}' is NTLM.");
credentials = BasicAuthentication.NtlmCredentials;
}
break;
}
if (credentials != null)
@ -902,8 +913,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))

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

@ -80,6 +80,7 @@ namespace Microsoft.Alm.Cli
private string _queryProtocol;
private TargetUri _targetUri;
private TimeSpan? _tokenDuration;
private string _urlOverride;
private bool _useHttpPath;
private bool _useLocalConfig;
private bool _useModalUi;
@ -163,6 +164,7 @@ namespace Microsoft.Alm.Cli
{
_gitRemoteHttpCommandLine = value;
// Re-create the target Uri.
CreateTargetUri();
}
}
@ -332,6 +334,23 @@ namespace Microsoft.Alm.Cli
set { _tokenDuration = value; }
}
/// <summary>
/// Gets or sets the override value for the `<seealso cref="TargetUri.ActualUri"/>` value.
/// <para/>
/// Default value is `<see langword="null"/>`.
/// </summary>
public virtual string UrlOverride
{
get { return _urlOverride; }
set
{
_urlOverride = value;
// Re-create the target Uri.
CreateTargetUri();
}
}
/// <summary>
/// Gets or sets `<see langword="false"/>` if `<seealso cref="LoadConfiguration"/>` ignores local Git configuration values; otherwise `<see langword="true"/>`.
/// <para/>
@ -435,7 +454,7 @@ namespace Microsoft.Alm.Cli
}
/// <summary>
///
/// Reads git-credential formatted input from `<paramref name="readableStream"/>`, parses the data, and populates `<seealso cref="TargetUri"/>`.
/// </summary>
/// <param name="readableStream">
/// Readable stream with credential protocol formatted information.
@ -576,9 +595,7 @@ namespace Microsoft.Alm.Cli
/// <param name="url">The uniform-resource-locater of the proxy.</param>
public virtual void SetProxy(string url)
{
Uri tmp = null;
if (Uri.TryCreate(url, UriKind.Absolute, out tmp))
if (Uri.TryCreate(url, UriKind.Absolute, out Uri tmp))
{
Trace.WriteLine($"successfully set proxy to '{tmp.AbsoluteUri}'.");
}
@ -621,7 +638,7 @@ namespace Microsoft.Alm.Cli
/// </summary>
public override string ToString()
{
StringBuilder builder = new StringBuilder();
var builder = new StringBuilder();
builder.Append("protocol=")
.Append(_queryProtocol ?? string.Empty)
@ -686,7 +703,7 @@ namespace Microsoft.Alm.Cli
string proxyUrl = _proxyUri?.OriginalString;
string actualUrl = null;
StringBuilder buffer = new StringBuilder();
var buffer = new StringBuilder();
// URI format is {protocol}://{username}@{host}/{path] with
// everything optional except for {host}.
@ -721,32 +738,52 @@ namespace Microsoft.Alm.Cli
queryUrl = buffer.ToString();
// If the actual-url override has been set, honor it.
if (!string.IsNullOrEmpty(_urlOverride))
{
if (Uri.TryCreate(_urlOverride, UriKind.Absolute, out Uri uri))
{
actualUrl = uri.ToString();
}
else
{
Trace.WriteLine($"failed to parse \"{_urlOverride}\", unable to set URL override.");
}
}
// If the git-remote-http(s) command line has been captured,
// try and parse it and provide the command-url .
if (!string.IsNullOrEmpty(_gitRemoteHttpCommandLine))
else if (!string.IsNullOrEmpty(_gitRemoteHttpCommandLine))
{
string[] parts = _gitRemoteHttpCommandLine.Split(' ');
switch(parts.Length)
switch (parts.Length)
{
case 1:
{
if (Uri.TryCreate(parts[0], UriKind.Absolute, out Uri uri))
{
if (Uri.TryCreate(parts[0], UriKind.Absolute, out Uri uri))
{
actualUrl = uri.ToString();
}
actualUrl = uri.ToString();
}
break;
else
{
Trace.WriteLine($"failed to parse \"{parts[0]}\", unable to set URL override.");
}
}
break;
case 3:
{
if (Uri.TryCreate(parts[2], UriKind.Absolute, out Uri uri))
{
if (Uri.TryCreate(parts[2], UriKind.Absolute, out Uri uri))
{
actualUrl = uri.ToString();
}
actualUrl = uri.ToString();
}
break;
}
else
{
Trace.WriteLine($"failed to parse \"{parts[2]}\", unable to set URL override.");
}
}
break;
}
}
// Create the target URI object.
@ -778,7 +815,7 @@ namespace Microsoft.Alm.Cli
case ',':
case ';':
case '=':
return true;
return true;
}
}
return false;

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

@ -53,6 +53,7 @@ namespace Microsoft.Alm.Cli
Namespace,
PreserveCredentials,
TokenDuration,
UrlOverride,
Username,
Validate,
VstsScope,
@ -117,19 +118,19 @@ namespace Microsoft.Alm.Cli
internal readonly Dictionary<KeyType, string> _configurationKeys = new Dictionary<KeyType, string>()
{
{ KeyType.Authority, "authority" },
{ KeyType.HttpProxy, "httpProxy" },
{ KeyType.HttpsProxy, "httpsProxy" },
{ KeyType.Interactive, "interactive" },
{ KeyType.ModalPrompt, "modalPrompt" },
{ KeyType.Namespace, "namespace" },
{ KeyType.PreserveCredentials, "preserve" },
{ KeyType.TokenDuration, "tokenDuration" },
{ KeyType.HttpPath, "useHttpPath" },
{ KeyType.Username, "username" },
{ KeyType.Validate, "validate" },
{ KeyType.VstsScope, "vstsScope" },
{ KeyType.Writelog, "writeLog" },
{ KeyType.Authority, "authority" },
{ KeyType.HttpProxy, "httpProxy" },
{ KeyType.HttpsProxy, "httpsProxy" },
{ KeyType.Interactive, "interactive" },
{ KeyType.ModalPrompt, "modalPrompt" },
{ KeyType.Namespace, "namespace" },
{ KeyType.PreserveCredentials, "preserve" },
{ KeyType.TokenDuration, "tokenDuration" },
{ KeyType.HttpPath, "useHttpPath" },
{ KeyType.Username, "username" },
{ KeyType.Validate, "validate" },
{ KeyType.VstsScope,"vstsScope" },
{ KeyType.Writelog, "writeLog" },
};
internal readonly Dictionary<KeyType, string> _environmentKeys = new Dictionary<KeyType, string>()
{
@ -142,11 +143,12 @@ namespace Microsoft.Alm.Cli
{ KeyType.Interactive, "GCM_INTERACTIVE" },
{ KeyType.ModalPrompt, "GCM_MODAL_PROMPT" },
{ KeyType.Namespace, "GCM_NAMESPACE" },
{ KeyType.ParentHwnd, "GCM_MODAL_PARENTHWND" },
{ KeyType.PreserveCredentials, "GCM_PRESERVE" },
{ KeyType.TokenDuration, "GCM_TOKEN_DURATION" },
{ KeyType.UrlOverride, "GCM_URL_OVERRIDE" },
{ KeyType.Validate, "GCM_VALIDATE" },
{ KeyType.VstsScope, "GCM_VSTS_SCOPE" },
{ KeyType.ParentHwnd, "GCM_MODAL_PARENTHWND" },
{ KeyType.Writelog, "GCM_WRITELOG" },
};
@ -475,7 +477,7 @@ namespace Microsoft.Alm.Cli
// If the value is true or a number greater than zero, then trace to standard error.
if (Git.Configuration.PaserBoolean(traceValue))
{
Trace.AddListener(Console.Error);
Trace.AddListener(Error);
}
// If the value is a rooted path, then trace to that file and not to the console.
else if (Path.IsPathRooted(traceValue))