Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cut new release #105

Merged
merged 29 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7c26c5f
Merge pull request #87 from microsoft/release
mjcheetham Dec 13, 2019
5b2b752
Add GCM_INTERACTIVE/credential.interactive setting
mjcheetham Jan 31, 2020
21abaa0
Merge pull request #91 from mjcheetham/nointeractive
mjcheetham Jan 31, 2020
3c183f9
Create and publish zip & tar.gz archives of app and symbols
mjcheetham Feb 13, 2020
be7feb7
Merge pull request #96 from microsoft/zippy
mjcheetham Feb 13, 2020
92e1d9e
Add ISystemPrompts component and impl for Windows
mjcheetham May 24, 2019
22da69c
Set parent window handle based on GCM_MODAL_PARENTHWD
mjcheetham Feb 12, 2020
8ef6914
Merge pull request #92 from mjcheetham/basic-nativeui
mjcheetham Feb 14, 2020
7a003ac
Add debug trace listener when debugger is attached
mjcheetham Feb 7, 2020
f64d3e9
Merge pull request #94 from mjcheetham/debug-trace
mjcheetham Apr 6, 2020
60fc632
build: update macOS build images
mjcheetham Apr 6, 2020
fca0adc
oauth: add an OAuth2 client implementation
mjcheetham Feb 4, 2020
637a056
oauth: add unit tests for OAuth2Client
mjcheetham Apr 14, 2020
0b0752d
Merge pull request #99 from mjcheetham/oauth2
mjcheetham Apr 16, 2020
bac4884
terminal: add a simple TTY-based menu system
mjcheetham Apr 8, 2020
e12fd6a
settings: pull generic 'get' methods up to interface
mjcheetham Apr 16, 2020
d5975df
github: use OAuth2/web flow for GitHub
mjcheetham Apr 8, 2020
d8cc644
github-ui: use new GitHub UI on Windows
mjcheetham Mar 30, 2020
7283e81
build: use latest .NET Core SDK 3.1.201 to build
mjcheetham Apr 17, 2020
98bde60
netcore: upgrade to .NET Core 3.1
mjcheetham Apr 17, 2020
5f9d449
build: ignore Windows projects in Mac sln configs
mjcheetham Apr 17, 2020
9ffdc5e
Merge pull request #101 from mjcheetham/github-oauth-ui
mjcheetham Apr 22, 2020
5f13ab2
oauth: implement RFC 7636 PKCE in OAuth client
mjcheetham Apr 21, 2020
adccfaf
oauth: ensure auth grant redirect URI matches token req
mjcheetham Apr 21, 2020
7efe852
Merge pull request #102 from mjcheetham/oauth2-pkce
mjcheetham Apr 23, 2020
e2d7b80
github: allow OAuth params to be overridden at runtime
mjcheetham Apr 24, 2020
9d17ca2
Merge pull request #103 from mjcheetham/gh-oauthenv
mjcheetham Apr 28, 2020
fcaec85
build: workaround Azure Pipelines build bug
mjcheetham Apr 28, 2020
ba53205
Merge pull request #106 from mjcheetham/fix-winbuild
mjcheetham Apr 28, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
settings: pull generic 'get' methods up to interface
Pull the general `TryGetSetting` and `GetSettingValues` methods up from
the `Settings` class to the `ISettings` interface for all providers to
use for their own settings.
  • Loading branch information
mjcheetham committed Apr 20, 2020
commit e12fd6a495cc2a91f1b755a002dd0cf0c1c83305
274 changes: 139 additions & 135 deletions src/shared/Microsoft.Git.CredentialManager/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,27 @@ namespace Microsoft.Git.CredentialManager
/// </summary>
public interface ISettings : IDisposable
{
/// <summary>
/// Try and get the value of a specified setting as specified in the environment and Git configuration,
/// with the environment taking precedence over Git.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <param name="value">Value of the requested setting.</param>
/// <returns>True if a setting value was found, false otherwise.</returns>
bool TryGetSetting(string envarName, string section, string property, out string value);

/// <summary>
/// Try and get the all values of a specified setting as specified in the environment and Git configuration,
/// in the correct order or precedence.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <returns>All values for the specified setting, in order of precedence, or an empty collection if no such values are set.</returns>
IEnumerable<string> GetSettingValues(string envarName, string section, string property);

/// <summary>
/// Git repository that local configuration lookup is scoped to, or null if this instance is not scoped to a repository.
/// </summary>
Expand Down Expand Up @@ -112,6 +133,124 @@ public Settings(IEnvironment environment, IGit git, string repositoryPath = null
RepositoryPath = repositoryPath;
}

public bool TryGetSetting(string envarName, string section, string property, out string value)
{
IEnumerable<string> allValues = GetSettingValues(envarName, section, property);

value = allValues.FirstOrDefault();

return value != null;
}

public IEnumerable<string> GetSettingValues(string envarName, string section, string property)
{
string value;

if (envarName != null)
{
if (_environment.Variables.TryGetValue(envarName, out value))
{
yield return value;
}
}

if (section != null && property != null)
{
IGitConfiguration config = GetGitConfiguration();

if (RemoteUri != null)
{
/*
* Look for URL scoped "section" configuration entries, starting from the most specific
* down to the least specific (stopping before the TLD).
*
* In a divergence from standard Git configuration rules, we also consider matching URL scopes
* without a scheme ("protocol://").
*
* For each level of scope, we look for an entry with the scheme included (the default), and then
* also one without it specified. This allows you to have one configuration scope for both "http" and
* "https" without needing to repeat yourself, for example.
*
* For example, starting with "https://foo.example.com/bar/buzz" we have:
*
* 1a. [section "https://foo.example.com/bar/buzz"]
* property = value
*
* 1b. [section "foo.example.com/bar/buzz"]
* property = value
*
* 2a. [section "https://foo.example.com/bar"]
* property = value
*
* 2b. [section "foo.example.com/bar"]
* property = value
*
* 3a. [section "https://foo.example.com"]
* property = value
*
* 3b. [section "foo.example.com"]
* property = value
*
* 4a. [section "https://example.com"]
* property = value
*
* 4b. [section "example.com"]
* property = value
*
*/

// Enumerate all configuration entries with the correct section and property name
// and make a local copy of them here to avoid needing to call `TryGetValue` on the
// IGitConfiguration object multiple times in a loop below.
var configEntries = new Dictionary<string, string>();
config.Enumerate((entryName, entryValue) =>
{
string entrySection = entryName.TruncateFromIndexOf('.');
string entryProperty = entryName.TrimUntilLastIndexOf('.');

if (StringComparer.OrdinalIgnoreCase.Equals(entrySection, section) &&
StringComparer.OrdinalIgnoreCase.Equals(entryProperty, property))
{
configEntries[entryName] = entryValue;
}

// Continue the enumeration
return true;
});

foreach (string scope in RemoteUri.GetGitConfigurationScopes())
{
string queryName = $"{section}.{scope}.{property}";
// Look for a scoped entry that includes the scheme "protocol://example.com" first as this is more specific
if (configEntries.TryGetValue(queryName, out value))
{
yield return value;
}

// Now look for a scoped entry that omits the scheme "example.com" second as this is less specific
string scopeWithoutScheme = scope.TrimUntilIndexOf(Uri.SchemeDelimiter);
string queryWithSchemeName = $"{section}.{scopeWithoutScheme}.{property}";
if (configEntries.TryGetValue(queryWithSchemeName, out value))
{
yield return value;
}
}
}

/*
* Try to look for an un-scoped "section" property setting:
*
* [section]
* property = value
*
*/
if (config.TryGetValue($"{section}.{property}", out value))
{
yield return value;
}
}
}

public string RepositoryPath { get; }

public Uri RemoteUri { get; set; }
Expand Down Expand Up @@ -279,141 +418,6 @@ bool TryGetUriSetting(string envarName, string section, string property, out Uri

public string ParentWindowId => _environment.Variables.TryGetValue(Constants.EnvironmentVariables.GcmParentWindow, out string parentWindowId) ? parentWindowId : null;

/// <summary>
/// Try and get the value of a specified setting as specified in the environment and Git configuration,
/// with the environment taking precedence over Git.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <param name="value">Value of the requested setting.</param>
/// <returns>True if a setting value was found, false otherwise.</returns>
public bool TryGetSetting(string envarName, string section, string property, out string value)
{
IEnumerable<string> allValues = GetSettingValues(envarName, section, property);

value = allValues.FirstOrDefault();

return value != null;
}

/// <summary>
/// Try and get the all values of a specified setting as specified in the environment and Git configuration,
/// in the correct order or precedence.
/// </summary>
/// <param name="envarName">Optional environment variable name.</param>
/// <param name="section">Optional Git configuration section name.</param>
/// <param name="property">Git configuration property name. Required if <paramref name="section"/> is set, optional otherwise.</param>
/// <returns>All values for the specified setting, in order of precedence, or an empty collection if no such values are set.</returns>
public IEnumerable<string> GetSettingValues(string envarName, string section, string property)
{
string value;

if (envarName != null)
{
if (_environment.Variables.TryGetValue(envarName, out value))
{
yield return value;
}
}

if (section != null && property != null)
{
IGitConfiguration config = GetGitConfiguration();

if (RemoteUri != null)
{
/*
* Look for URL scoped "section" configuration entries, starting from the most specific
* down to the least specific (stopping before the TLD).
*
* In a divergence from standard Git configuration rules, we also consider matching URL scopes
* without a scheme ("protocol://").
*
* For each level of scope, we look for an entry with the scheme included (the default), and then
* also one without it specified. This allows you to have one configuration scope for both "http" and
* "https" without needing to repeat yourself, for example.
*
* For example, starting with "https://foo.example.com/bar/buzz" we have:
*
* 1a. [section "https://foo.example.com/bar/buzz"]
* property = value
*
* 1b. [section "foo.example.com/bar/buzz"]
* property = value
*
* 2a. [section "https://foo.example.com/bar"]
* property = value
*
* 2b. [section "foo.example.com/bar"]
* property = value
*
* 3a. [section "https://foo.example.com"]
* property = value
*
* 3b. [section "foo.example.com"]
* property = value
*
* 4a. [section "https://example.com"]
* property = value
*
* 4b. [section "example.com"]
* property = value
*
*/

// Enumerate all configuration entries with the correct section and property name
// and make a local copy of them here to avoid needing to call `TryGetValue` on the
// IGitConfiguration object multiple times in a loop below.
var configEntries = new Dictionary<string, string>();
config.Enumerate((entryName, entryValue) =>
{
string entrySection = entryName.TruncateFromIndexOf('.');
string entryProperty = entryName.TrimUntilLastIndexOf('.');

if (StringComparer.OrdinalIgnoreCase.Equals(entrySection, section) &&
StringComparer.OrdinalIgnoreCase.Equals(entryProperty, property))
{
configEntries[entryName] = entryValue;
}

// Continue the enumeration
return true;
});

foreach (string scope in RemoteUri.GetGitConfigurationScopes())
{
string queryName = $"{section}.{scope}.{property}";
// Look for a scoped entry that includes the scheme "protocol://example.com" first as this is more specific
if (configEntries.TryGetValue(queryName, out value))
{
yield return value;
}

// Now look for a scoped entry that omits the scheme "example.com" second as this is less specific
string scopeWithoutScheme = scope.TrimUntilIndexOf(Uri.SchemeDelimiter);
string queryWithSchemeName = $"{section}.{scopeWithoutScheme}.{property}";
if (configEntries.TryGetValue(queryWithSchemeName, out value))
{
yield return value;
}
}
}

/*
* Try to look for an un-scoped "section" property setting:
*
* [section]
* property = value
*
*/
if (config.TryGetValue($"{section}.{property}", out value))
{
yield return value;
}
}
}

private IGitConfiguration GetGitConfiguration() => _gitConfig ?? (_gitConfig = _git.GetConfiguration(RepositoryPath));

#region IDisposable
Expand Down
38 changes: 27 additions & 11 deletions src/shared/TestInfrastructure/Objects/TestCommandContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,33 @@ namespace Microsoft.Git.CredentialManager.Tests.Objects
{
public class TestCommandContext : ICommandContext
{
public TestSettings Settings { get; set; } = new TestSettings();
public TestStandardStreams Streams { get; set; } = new TestStandardStreams();
public TestTerminal Terminal { get; set; } = new TestTerminal();
public bool IsDesktopSession { get; set; } = true;
public ITrace Trace { get; set; } = new NullTrace();
public TestFileSystem FileSystem { get; set; } = new TestFileSystem();
public TestCredentialStore CredentialStore { get; set; } = new TestCredentialStore();
public TestHttpClientFactory HttpClientFactory { get; set; } = new TestHttpClientFactory();
public TestGit Git { get; set; } = new TestGit();
public TestEnvironment Environment { get; set; } = new TestEnvironment();
public TestSystemPrompts SystemPrompts { get; set; } = new TestSystemPrompts();
public TestCommandContext()
{
Streams = new TestStandardStreams();
Terminal = new TestTerminal();
IsDesktopSession = true;
Trace = new NullTrace();
FileSystem = new TestFileSystem();
CredentialStore = new TestCredentialStore();
HttpClientFactory = new TestHttpClientFactory();
Git = new TestGit();
Environment = new TestEnvironment();
SystemPrompts = new TestSystemPrompts();

Settings = new TestSettings {Environment = Environment, GitConfiguration = Git.GlobalConfiguration};
}

public TestSettings Settings { get; set; }
public TestStandardStreams Streams { get; set; }
public TestTerminal Terminal { get; set; }
public bool IsDesktopSession { get; set; }
public ITrace Trace { get; set; }
public TestFileSystem FileSystem { get; set; }
public TestCredentialStore CredentialStore { get; set; }
public TestHttpClientFactory HttpClientFactory { get; set; }
public TestGit Git { get; set; }
public TestEnvironment Environment { get; set; }
public TestSystemPrompts SystemPrompts { get; set; }

#region ICommandContext

Expand Down
Loading