Skip to content

Commit

Permalink
Merge pull request #217 from mjcheetham/ntlm-proxy-auth
Browse files Browse the repository at this point in the history
Treat empty credential proxy configuration as use system default credentials
  • Loading branch information
mjcheetham authored Nov 12, 2020
2 parents edfb863 + 4f91faa commit c3a543a
Show file tree
Hide file tree
Showing 2 changed files with 108 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,104 @@ public void HttpClientFactory_TryCreateProxy_ProxyWithCredentials_ReturnsTrueOut
Assert.Equal(proxyPass, configuredCredentials.Password);
}

[Fact]
public void HttpClientFactory_TryCreateProxy_ProxyWithNonEmptyUserAndEmptyPass_ReturnsTrueOutProxyWithUrlConfiguredCredentials()
{
const string proxyScheme = "https";
const string proxyUser = "john.doe";
const string proxyHost = "proxy.example.com/git";
const string repoPath = "/tmp/repos/foo";
const string repoRemote = "https://remote.example.com/foo.git";

string proxyConfigString = $"{proxyScheme}://{proxyUser}:@{proxyHost}";
string expectedProxyUrl = $"{proxyScheme}://{proxyHost}";
var repoRemoteUri = new Uri(repoRemote);

var settings = new TestSettings
{
RemoteUri = repoRemoteUri,
RepositoryPath = repoPath,
ProxyConfiguration = new Uri(proxyConfigString)
};
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());

bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);

Assert.True(result);
Assert.NotNull(proxy);
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());

Assert.NotNull(proxy.Credentials);
Assert.IsType<NetworkCredential>(proxy.Credentials);
var configuredCredentials = (NetworkCredential) proxy.Credentials;
Assert.Equal(proxyUser, configuredCredentials.UserName);
Assert.True(string.IsNullOrWhiteSpace(configuredCredentials.Password));
}

[Fact]
public void HttpClientFactory_TryCreateProxy_ProxyWithEmptyUserAndNonEmptyPass_ReturnsTrueOutProxyWithUrlConfiguredCredentials()
{
const string proxyScheme = "https";
const string proxyPass = "letmein";
const string proxyHost = "proxy.example.com/git";
const string repoPath = "/tmp/repos/foo";
const string repoRemote = "https://remote.example.com/foo.git";

string proxyConfigString = $"{proxyScheme}://:{proxyPass}@{proxyHost}";
string expectedProxyUrl = $"{proxyScheme}://{proxyHost}";
var repoRemoteUri = new Uri(repoRemote);

var settings = new TestSettings
{
RemoteUri = repoRemoteUri,
RepositoryPath = repoPath,
ProxyConfiguration = new Uri(proxyConfigString)
};
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());

bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);

Assert.True(result);
Assert.NotNull(proxy);
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());

Assert.NotNull(proxy.Credentials);
Assert.IsType<NetworkCredential>(proxy.Credentials);
var configuredCredentials = (NetworkCredential) proxy.Credentials;
Assert.True(string.IsNullOrWhiteSpace(configuredCredentials.UserName));
Assert.Equal(proxyPass, configuredCredentials.Password);
}

[Fact]
public void HttpClientFactory_TryCreateProxy_ProxyEmptyUserAndEmptyPass_ReturnsTrueOutProxyWithUrlDefaultCredentials()
{
const string repoPath = "/tmp/repos/foo";
const string repoRemote = "https://remote.example.com/foo.git";
var repoRemoteUri = new Uri(repoRemote);

string proxyConfigString = "https://:@proxy.example.com/git";
string expectedProxyUrl = "https://proxy.example.com/git";

var settings = new TestSettings
{
RemoteUri = repoRemoteUri,
RepositoryPath = repoPath,
ProxyConfiguration = new Uri(proxyConfigString)
};
var httpFactory = new HttpClientFactory(Mock.Of<ITrace>(), settings, Mock.Of<IStandardStreams>());

bool result = httpFactory.TryCreateProxy(out IWebProxy proxy);

Assert.True(result);
Assert.NotNull(proxy);
var configuredProxyUrl = proxy.GetProxy(repoRemoteUri);
Assert.Equal(expectedProxyUrl, configuredProxyUrl.ToString());

AssertDefaultCredentials(proxy.Credentials);
}

private static void AssertDefaultCredentials(ICredentials credentials)
{
var netCred = (NetworkCredential) credentials;
Expand Down
14 changes: 10 additions & 4 deletions src/shared/Microsoft.Git.CredentialManager/HttpClientFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,24 +120,30 @@ public bool TryCreateProxy(out IWebProxy proxy)
// Dictionary of proxy info for tracing
var dict = new Dictionary<string, string> {["uri"] = proxyUri.ToString()};

// Try to extract and configure proxy credentials from the configured URI
if (proxyConfig.TryGetUserInfo(out string userName, out string password))
// Try to extract and configure proxy credentials from the configured URI.
// For an empty username AND password we should use the system default credentials
// (for example for Windows Integrated Authentication-based proxies).
if (proxyConfig.TryGetUserInfo(out string userName, out string password) &&
!(string.IsNullOrEmpty(userName) && string.IsNullOrEmpty(password)))
{
proxy = new WebProxy(proxyUri)
{
Credentials = new NetworkCredential(userName, password)
};

// Add user/pass info to the trace dictionary
if (!string.IsNullOrWhiteSpace(userName)) dict["username"] = userName;
if (!string.IsNullOrEmpty(password)) dict["password"] = password;
dict["username"] = userName;
dict["password"] = password;
}
else
{
proxy = new WebProxy(proxyUri)
{
UseDefaultCredentials = true
};

// Trace the use of system default credentials
dict["useDefaultCredentials"] = "true";
}

// Tracer out proxy info dictionary
Expand Down

0 comments on commit c3a543a

Please sign in to comment.