Skip to content

Commit af15e18

Browse files
authored
Merge pull request #16 from mjcheetham/windows-msauth-helper
Add Microsoft Authentication helper for Windows
2 parents 29037f8 + d24e303 commit af15e18

File tree

22 files changed

+861
-121
lines changed

22 files changed

+861
-121
lines changed

Git-Credential-Manager.sln

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub", "common\src\GitHub
3131
EndProject
3232
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHub.Tests", "common\tests\GitHub.Tests\GitHub.Tests.csproj", "{03B9B82B-7DCA-4554-97AB-C98FF18FB385}"
3333
EndProject
34+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Authentication.Helper", "windows\src\Microsoft.Authentication.Helper\Microsoft.Authentication.Helper.csproj", "{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}"
35+
EndProject
36+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.Authentication.Helper.Tests", "windows\tests\Microsoft.Authentication.Helper.Tests\Microsoft.Authentication.Helper.Tests.csproj", "{E0391B02-16D5-4B49-9C33-349B25717011}"
37+
EndProject
3438
Global
3539
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3640
Debug|Any CPU = Debug|Any CPU
@@ -103,6 +107,18 @@ Global
103107
{03B9B82B-7DCA-4554-97AB-C98FF18FB385}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
104108
{03B9B82B-7DCA-4554-97AB-C98FF18FB385}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
105109
{03B9B82B-7DCA-4554-97AB-C98FF18FB385}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
110+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
111+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}.Release|Any CPU.ActiveCfg = Release|Any CPU
112+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
113+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
114+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
115+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
116+
{E0391B02-16D5-4B49-9C33-349B25717011}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
117+
{E0391B02-16D5-4B49-9C33-349B25717011}.Release|Any CPU.ActiveCfg = Release|Any CPU
118+
{E0391B02-16D5-4B49-9C33-349B25717011}.WindowsDebug|Any CPU.ActiveCfg = Debug|Any CPU
119+
{E0391B02-16D5-4B49-9C33-349B25717011}.WindowsDebug|Any CPU.Build.0 = Debug|Any CPU
120+
{E0391B02-16D5-4B49-9C33-349B25717011}.WindowsRelease|Any CPU.ActiveCfg = Release|Any CPU
121+
{E0391B02-16D5-4B49-9C33-349B25717011}.WindowsRelease|Any CPU.Build.0 = Release|Any CPU
106122
EndGlobalSection
107123
GlobalSection(SolutionProperties) = preSolution
108124
HideSolutionNode = FALSE
@@ -120,6 +136,8 @@ Global
120136
{5A7D9E8B-C1D2-4C5C-BE98-648C41D1F8BD} = {4B305AC9-153F-4EA3-822F-3E5023BABAF1}
121137
{3C840B06-A595-4FD9-9A76-56CD45B14780} = {A7FC1234-95E3-4496-B5F7-4306F41E6A0E}
122138
{03B9B82B-7DCA-4554-97AB-C98FF18FB385} = {4B305AC9-153F-4EA3-822F-3E5023BABAF1}
139+
{8B984F78-4EAF-4BC0-A34E-BA3949700ED4} = {1229E443-E66C-402F-8AA4-5AE6A207D3C7}
140+
{E0391B02-16D5-4B49-9C33-349B25717011} = {DD8B847B-286B-4928-867B-E09C6C90DA1F}
123141
EndGlobalSection
124142
GlobalSection(ExtensibilityGlobals) = postSolution
125143
SolutionGuid = {0EF9FC65-E6BA-45D4-A455-262A9EA4366B}

common/src/Git-Credential-Manager/Git-Credential-Manager.csproj

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
55
<TargetFramework>netcoreapp2.1</TargetFramework>
6+
<TargetFramework Condition="'$(OS)'!='Unix'">net461</TargetFramework>
67
<RuntimeIdentifiers>win-x64;osx-x64</RuntimeIdentifiers>
78
<AssemblyName>git-credential-manager</AssemblyName>
89
<RootNamespace>Microsoft.Git.CredentialManager</RootNamespace>
@@ -16,4 +17,8 @@
1617
<ProjectReference Include="..\Microsoft.Git.CredentialManager\Microsoft.Git.CredentialManager.csproj" />
1718
</ItemGroup>
1819

20+
<ItemGroup Condition="'$(TargetFramework)'=='net461'">
21+
<ProjectReference Include="..\..\..\windows\src\Microsoft.Authentication.Helper\Microsoft.Authentication.Helper.csproj" />
22+
</ItemGroup>
23+
1924
</Project>

common/src/GitHub/GitHub.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFrameworks>netstandard2.0</TargetFrameworks>
5+
<TargetFrameworks Condition="'$(OS)'!='Unix'">netstandard2.0;net461</TargetFrameworks>
56
<RootNamespace>GitHub</RootNamespace>
67
<AssemblyName>GitHub</AssemblyName>
78
<IsTestProject>false</IsTestProject>
@@ -12,4 +13,8 @@
1213
<ProjectReference Include="..\Microsoft.Git.CredentialManager\Microsoft.Git.CredentialManager.csproj" />
1314
</ItemGroup>
1415

16+
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
17+
<Reference Include="System.Net.Http" />
18+
</ItemGroup>
19+
1520
</Project>

common/src/Microsoft.AzureRepos/Microsoft.AzureRepos.csproj

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFramework>netstandard2.0</TargetFramework>
4+
<TargetFrameworks>netstandard2.0</TargetFrameworks>
5+
<TargetFrameworks Condition="'$(OS)'!='Unix'">netstandard2.0;net461</TargetFrameworks>
56
<IsTestProject>false</IsTestProject>
67
<LangVersion>latest</LangVersion>
78
<RootNamespace>Microsoft.AzureRepos</RootNamespace>
@@ -12,4 +13,8 @@
1213
<ProjectReference Include="..\Microsoft.Git.CredentialManager\Microsoft.Git.CredentialManager.csproj" />
1314
</ItemGroup>
1415

16+
<ItemGroup Condition="'$(TargetFramework)' == 'net461'">
17+
<Reference Include="System.Net.Http" />
18+
</ItemGroup>
19+
1520
</Project>
Lines changed: 20 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,20 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT license.
33
using System;
4-
using System.Diagnostics;
5-
using System.IO;
6-
using System.Text;
74
using System.Threading.Tasks;
85
using Microsoft.Git.CredentialManager.Commands;
96

107
namespace Microsoft.Git.CredentialManager
118
{
12-
public class Application : IDisposable
9+
public class Application : ApplicationBase
1310
{
14-
private readonly ICommandContext _context;
15-
16-
private TextWriter _traceFileWriter;
17-
1811
public IHostProviderRegistry ProviderRegistry { get; } = new HostProviderRegistry();
1912

2013
public Application(ICommandContext context)
21-
{
22-
EnsureArgument.NotNull(context, nameof(context));
23-
24-
_context = context;
25-
}
14+
: base(context) { }
2615

27-
public async Task<int> RunAsync(string[] args)
16+
protected override async Task<int> RunInternalAsync(string[] args)
2817
{
29-
// Launch debugger
30-
if (_context.IsEnvironmentVariableTruthy(Constants.EnvironmentVariables.GcmDebug, false))
31-
{
32-
_context.StdError.WriteLine("Waiting for debugger to be attached...");
33-
PlatformUtils.WaitForDebuggerAttached();
34-
35-
// Now the debugger is attached, break!
36-
Debugger.Break();
37-
}
38-
39-
// Enable tracing
40-
if (_context.TryGetEnvironmentVariable(Constants.EnvironmentVariables.GcmTrace, out string traceEnvar))
41-
{
42-
if (traceEnvar.IsTruthy()) // Trace to stderr
43-
{
44-
_context.Trace.AddListener(_context.StdError);
45-
}
46-
else if (Path.IsPathRooted(traceEnvar) && // Trace to a file
47-
TryCreateTextWriter(_context, traceEnvar, out var fileWriter))
48-
{
49-
_traceFileWriter = fileWriter;
50-
_context.Trace.AddListener(fileWriter);
51-
}
52-
else
53-
{
54-
_context.StdError.WriteLine($"warning: cannot write trace output to {traceEnvar}");
55-
}
56-
}
57-
58-
// Enable sensitive tracing and show warning
59-
if (_context.IsEnvironmentVariableTruthy(Constants.EnvironmentVariables.GcmTraceSecrets, false))
60-
{
61-
_context.Trace.EnableSecretTracing = true;
62-
_context.StdError.WriteLine("Secret tracing is enabled. Trace output may contain sensitive information.");
63-
}
64-
6518
// Construct all supported commands
6619
var commands = new CommandBase[]
6720
{
@@ -73,12 +26,12 @@ public async Task<int> RunAsync(string[] args)
7326
};
7427

7528
// Trace the current version and program arguments
76-
_context.Trace.WriteLine($"{Constants.GetProgramHeader()} '{string.Join(" ", args)}'");
29+
Context.Trace.WriteLine($"{Constants.GetProgramHeader()} '{string.Join(" ", args)}'");
7730

7831
if (args.Length == 0)
7932
{
80-
_context.StdError.WriteLine("Missing command.");
81-
HelpCommand.PrintUsage(_context.StdError);
33+
Context.StdError.WriteLine("Missing command.");
34+
HelpCommand.PrintUsage(Context.StdError);
8235
return -1;
8336
}
8437

@@ -88,72 +41,49 @@ public async Task<int> RunAsync(string[] args)
8841
{
8942
try
9043
{
91-
await cmd.ExecuteAsync(_context, args);
44+
await cmd.ExecuteAsync(Context, args);
9245
return 0;
9346
}
9447
catch (Exception e)
9548
{
9649
if (e is AggregateException ae)
9750
{
98-
ae.Handle(x => WriteException(_context, x));
51+
ae.Handle(WriteException);
9952
}
10053
else
10154
{
102-
WriteException(_context, e);
55+
WriteException(e);
10356
}
10457

10558
return -1;
10659
}
10760
}
10861
}
10962

110-
_context.StdError.WriteLine("Unrecognized command '{0}'.", args[0]);
111-
HelpCommand.PrintUsage(_context.StdError);
63+
Context.StdError.WriteLine("Unrecognized command '{0}'.", args[0]);
64+
HelpCommand.PrintUsage(Context.StdError);
11265
return -1;
11366
}
11467

115-
#region Helpers
116-
117-
private static bool WriteException(ICommandContext context, Exception e)
68+
protected override void Dispose(bool disposing)
11869
{
119-
context.StdError.WriteLine("fatal: {0}", e.Message);
120-
if (e.InnerException != null)
70+
if (disposing)
12171
{
122-
context.StdError.WriteLine("fatal: {0}", e.InnerException.Message);
72+
ProviderRegistry.Dispose();
12373
}
12474

125-
return true;
75+
base.Dispose(disposing);
12676
}
12777

128-
private static bool TryCreateTextWriter(ICommandContext context, string path, out TextWriter writer)
78+
protected bool WriteException(Exception e)
12979
{
130-
writer = null;
131-
132-
try
133-
{
134-
var utf8NoBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
135-
136-
var stream = context.FileSystem.OpenFileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
137-
writer = new StreamWriter(stream, utf8NoBomEncoding, 4096, leaveOpen: false);
138-
}
139-
catch
80+
Context.StdError.WriteLine("fatal: {0}", e.Message);
81+
if (e.InnerException != null)
14082
{
141-
// Swallow all exceptions
83+
Context.StdError.WriteLine("fatal: {0}", e.InnerException.Message);
14284
}
14385

144-
return writer != null;
145-
}
146-
147-
#endregion
148-
149-
#region IDisposable
150-
151-
public void Dispose()
152-
{
153-
ProviderRegistry.Dispose();
154-
_traceFileWriter?.Dispose();
86+
return true;
15587
}
156-
157-
#endregion
15888
}
15989
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
using System;
2+
using System.Diagnostics;
3+
using System.IO;
4+
using System.Text;
5+
using System.Threading;
6+
using System.Threading.Tasks;
7+
8+
namespace Microsoft.Git.CredentialManager
9+
{
10+
public abstract class ApplicationBase : IDisposable
11+
{
12+
private static readonly Encoding Utf8NoBomEncoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false);
13+
14+
private TextWriter _traceFileWriter;
15+
16+
protected ICommandContext Context { get; }
17+
18+
protected ApplicationBase(ICommandContext context)
19+
{
20+
EnsureArgument.NotNull(context, nameof(context));
21+
22+
Context = context;
23+
}
24+
25+
public Task<int> RunAsync(string[] args)
26+
{
27+
// Launch debugger
28+
if (Context.IsEnvironmentVariableTruthy(Constants.EnvironmentVariables.GcmDebug, false))
29+
{
30+
Context.StdError.WriteLine("Waiting for debugger to be attached...");
31+
WaitForDebuggerAttached();
32+
33+
// Now the debugger is attached, break!
34+
Debugger.Break();
35+
}
36+
37+
// Enable tracing
38+
if (Context.TryGetEnvironmentVariable(Constants.EnvironmentVariables.GcmTrace, out string traceEnvar))
39+
{
40+
if (traceEnvar.IsTruthy()) // Trace to stderr
41+
{
42+
Context.Trace.AddListener(Context.StdError);
43+
}
44+
else if (Path.IsPathRooted(traceEnvar)) // Trace to a file
45+
{
46+
try
47+
{
48+
Stream stream = Context.FileSystem.OpenFileStream(traceEnvar, FileMode.Append, FileAccess.Write, FileShare.ReadWrite);
49+
_traceFileWriter = new StreamWriter(stream, Utf8NoBomEncoding, 4096, leaveOpen: false);
50+
51+
Context.Trace.AddListener(_traceFileWriter);
52+
}
53+
catch (Exception ex)
54+
{
55+
Context.StdError.WriteLine($"warning: unable to trace to file '{traceEnvar}': {ex.Message}");
56+
}
57+
}
58+
else
59+
{
60+
Context.StdError.WriteLine($"warning: unknown value for {Constants.EnvironmentVariables.GcmTrace} '{traceEnvar}'");
61+
}
62+
}
63+
64+
// Enable sensitive tracing and show warning
65+
if (Context.IsEnvironmentVariableTruthy(Constants.EnvironmentVariables.GcmTraceSecrets, false))
66+
{
67+
Context.Trace.IsSecretTracingEnabled = true;
68+
Context.Trace.WriteLine("Tracing of secrets is enabled. Trace output may contain sensitive information.");
69+
}
70+
71+
return RunInternalAsync(args);
72+
}
73+
74+
protected abstract Task<int> RunInternalAsync(string[] args);
75+
76+
#region Helpers
77+
78+
/// <summary>
79+
/// Wait until a debugger has attached to the currently executing process.
80+
/// </summary>
81+
private static void WaitForDebuggerAttached()
82+
{
83+
// Attempt to launch the debugger if the OS supports the explicit launching
84+
if (!Debugger.Launch())
85+
{
86+
// The prompt to debug was declined
87+
return;
88+
}
89+
90+
// Some platforms do not support explicit debugger launching..
91+
// Wait for the debugger to attach - poll & sleep until then
92+
while (!Debugger.IsAttached)
93+
{
94+
Thread.Sleep(TimeSpan.FromSeconds(1));
95+
}
96+
}
97+
98+
#endregion
99+
100+
#region IDisposable
101+
102+
protected virtual void Dispose(bool disposing)
103+
{
104+
if (disposing)
105+
{
106+
_traceFileWriter?.Dispose();
107+
}
108+
}
109+
110+
public void Dispose()
111+
{
112+
Dispose(true);
113+
GC.SuppressFinalize(this);
114+
}
115+
116+
#endregion
117+
}
118+
}

0 commit comments

Comments
 (0)