Skip to content

Commit 047d50e

Browse files
committed
make code compatible with VS2019
1 parent 3e215cd commit 047d50e

File tree

3 files changed

+194
-165
lines changed

3 files changed

+194
-165
lines changed

ClientCertificateManager.cs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
using System.Security.Cryptography.X509Certificates;
22
using Microsoft.VisualStudio.Services.Common;
33

4-
class ClientCertificateManager : IVssClientCertificateManager
4+
namespace SpecSync.AzureDevOps.ConnectionTester
55
{
6-
public X509Certificate2Collection ClientCertificates { get; } = new();
6+
class ClientCertificateManager : IVssClientCertificateManager
7+
{
8+
public X509Certificate2Collection ClientCertificates { get; } = new();
9+
}
710
}

Program.cs

Lines changed: 188 additions & 160 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1-
using System.ComponentModel;
1+
using System;
2+
using System.ComponentModel;
3+
using System.IO;
4+
using System.Linq;
25
using System.Net;
6+
using System.Net.Http;
37
using System.Net.Http.Headers;
48
using Microsoft.VisualStudio.Services.WebApi;
59
using System.Text;
@@ -8,173 +12,197 @@
812
using Microsoft.TeamFoundation.Core.WebApi;
913
using Microsoft.VisualStudio.Services.Common;
1014

11-
void PrintError(Exception exception, string indent = "")
15+
namespace SpecSync.AzureDevOps.ConnectionTester
1216
{
13-
var errorCode = "";
14-
if (exception is Win32Exception win32Exception)
17+
static class Program
1518
{
16-
errorCode = $" (0x{win32Exception.NativeErrorCode:x8})";
17-
}
18-
Console.WriteLine($"{indent}{exception.GetType().FullName}{errorCode}: {exception.Message}");
19-
if (exception is AggregateException aggregateException)
20-
{
21-
foreach (var innerEx in aggregateException.InnerExceptions)
19+
public static void Main(string[] args)
2220
{
23-
PrintError(innerEx, indent + " ---> ");
24-
}
25-
}
26-
else if (exception.InnerException != null)
27-
{
28-
PrintError(exception.InnerException, indent + " ---> ");
29-
}
30-
31-
//if (indent == "")
32-
//{
33-
// Console.WriteLine();
34-
// Console.WriteLine(exception);
35-
//}
36-
}
3721

38-
X509Certificate LoadClientCertificate(string filePath)
39-
{
40-
try
41-
{
42-
return new X509Certificate2(filePath);
43-
}
44-
catch (Exception)
45-
{
46-
Console.Write("Please specify password for client certificate or leave empty if no password required: ");
47-
var certPassword = Console.ReadLine();
48-
return new X509Certificate2(filePath, certPassword);
49-
}
50-
}
22+
Console.WriteLine("*** SpecSync for Azure DevOps Connection Tester ***");
23+
Console.WriteLine();
24+
25+
if (args.Length < 2)
26+
{
27+
Console.WriteLine("Usage:");
28+
Console.WriteLine(
29+
" SpecSync.ConnectionTester.exe <project-url> <pat> [<client-certificate-file>.pfx]");
30+
Console.WriteLine("OR");
31+
Console.WriteLine(
32+
" SpecSync.ConnectionTester.exe <project-url> <username> <password> [<client-certificate-file>.pfx]");
33+
return;
34+
}
35+
36+
var projectUrl = args[0];
37+
var userNameOrPat = args[1];
38+
var password = args.Length == 2 || args[2].EndsWith(".pfx", StringComparison.InvariantCultureIgnoreCase)
39+
? ""
40+
: args[2];
41+
var clientCertificateFile =
42+
args.Last().EndsWith(".pfx", StringComparison.InvariantCultureIgnoreCase) ? args.Last() : null;
43+
44+
ParseAdoProjectUrl(projectUrl, out var collectionUrl, out var projectName);
45+
46+
if (clientCertificateFile != null)
47+
{
48+
clientCertificateFile = Path.GetFullPath(clientCertificateFile);
49+
if (!File.Exists(clientCertificateFile))
50+
throw new InvalidOperationException(
51+
$"Client certificate file does not exist: {clientCertificateFile}");
52+
}
53+
54+
Console.WriteLine($"Collection URL: {collectionUrl}");
55+
Console.WriteLine($"Project Name: {projectName}");
56+
if (clientCertificateFile != null)
57+
Console.WriteLine($"Client certificate file: {clientCertificateFile}");
58+
Console.WriteLine();
59+
60+
var clientCertificate = clientCertificateFile != null ? LoadClientCertificate(clientCertificateFile) : null;
61+
62+
Console.WriteLine("Testing connection with Azure DevOps .NET API...");
63+
64+
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
65+
66+
try
67+
{
68+
var vssConnection = CreateVssConnection(new Uri(collectionUrl),
69+
new VssBasicCredential(userNameOrPat, password),
70+
clientCertificate);
71+
vssConnection.ConnectAsync().Wait();
72+
vssConnection.GetClient<ProjectHttpClient>().GetProject(projectName).Wait();
73+
Console.WriteLine("Succeeded!");
74+
}
75+
catch (Exception ex)
76+
{
77+
Console.WriteLine("Failed!");
78+
PrintError(ex);
79+
}
80+
81+
Console.WriteLine();
82+
Console.WriteLine();
83+
Console.WriteLine("Testing connection with HttpClient...");
84+
try
85+
{
86+
var handler = new HttpClientHandler();
87+
if (clientCertificate != null)
88+
handler.ClientCertificates.Add(clientCertificate);
89+
else
90+
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
91+
92+
var httpClient = new HttpClient(handler);
93+
httpClient.BaseAddress = new Uri(collectionUrl + "/");
94+
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic",
95+
Convert.ToBase64String(Encoding.UTF8.GetBytes(userNameOrPat + ":" + password)));
96+
var response = httpClient.GetAsync($"_apis/projects/{projectName}?includeHistory=False").Result;
97+
Console.WriteLine($" {response.RequestMessage?.RequestUri}");
98+
Console.WriteLine($" {(int)response.StatusCode} ({response.StatusCode})");
99+
if (response.StatusCode != HttpStatusCode.OK)
100+
Console.WriteLine("Failed! Wrong HTTP status code.");
101+
else
102+
Console.WriteLine("Succeeded!");
103+
}
104+
catch (Exception ex)
105+
{
106+
Console.WriteLine("Failed!");
107+
PrintError(ex);
108+
}
109+
110+
Console.WriteLine();
111+
Console.WriteLine();
112+
}
51113

52-
void ParseAdoProjectUrl(string url, out string adoCollectionUrl, out string adoProjectName)
53-
{
54-
url = url.TrimEnd('/');
55-
var lastSlash = url.LastIndexOf('/');
56-
if (lastSlash < 0)
57-
{
58-
throw new InvalidOperationException($"Unable to parse Azure DevOps project URL: {url}");
59-
}
60-
adoCollectionUrl = url.Substring(0, lastSlash);
61-
adoProjectName = url.Substring(lastSlash + 1);
62-
adoProjectName = WebUtility.UrlDecode(adoProjectName);
63-
}
114+
static VssConnection CreateVssConnection(Uri adoCollectionUrl, VssCredentials credentials, X509Certificate clientCertificate)
115+
{
116+
var vssHttpRequestSettings = VssClientHttpRequestSettings.Default.Clone();
117+
vssHttpRequestSettings.ServerCertificateValidationCallback = ServerCertificateValidationCallback;
118+
if (clientCertificate != null)
119+
{
120+
vssHttpRequestSettings.ClientCertificateManager = new ClientCertificateManager();
121+
vssHttpRequestSettings.ClientCertificateManager.ClientCertificates.Add(clientCertificate);
122+
}
123+
124+
var httpMessageHandlers = Enumerable.Empty<DelegatingHandler>();
125+
126+
var innerHandler = new HttpClientHandler();
127+
var vssHttpMessageHandler = new VssHttpMessageHandler(credentials, vssHttpRequestSettings, innerHandler);
128+
if (clientCertificate == null)
129+
innerHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
130+
return new VssConnection(adoCollectionUrl,
131+
vssHttpMessageHandler,
132+
httpMessageHandlers);
133+
}
64134

65-
VssConnection CreateVssConnection(Uri adoCollectionUrl, VssCredentials credentials, X509Certificate? clientCertificate)
66-
{
67-
var vssHttpRequestSettings = VssClientHttpRequestSettings.Default.Clone();
68-
vssHttpRequestSettings.ServerCertificateValidationCallback = ServerCertificateValidationCallback;
69-
if (clientCertificate != null)
70-
{
71-
vssHttpRequestSettings.ClientCertificateManager = new ClientCertificateManager();
72-
vssHttpRequestSettings.ClientCertificateManager.ClientCertificates.Add(clientCertificate);
73-
}
135+
static X509Certificate LoadClientCertificate(string filePath)
136+
{
137+
try
138+
{
139+
return new X509Certificate2(filePath);
140+
}
141+
catch (Exception)
142+
{
143+
Console.Write(
144+
"Please specify password for client certificate or leave empty if no password required: ");
145+
var certPassword = Console.ReadLine();
146+
return new X509Certificate2(filePath, certPassword);
147+
}
148+
}
74149

75-
var httpMessageHandlers = Enumerable.Empty<DelegatingHandler>();
150+
static bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain,
151+
SslPolicyErrors errors)
152+
{
153+
if (errors == SslPolicyErrors.None)
154+
{
155+
Console.WriteLine(" SSL validation passed.");
156+
return true;
157+
}
158+
159+
var hashString = certificate.GetCertHashString();
160+
Console.WriteLine($"SSL policy error(s) '{errors}' found for certificate thumbprint '{hashString}'.");
161+
Console.WriteLine(" SSL validation failed. Ignoring...");
162+
return true;
163+
}
76164

77-
var innerHandler = new HttpClientHandler();
78-
var vssHttpMessageHandler = new VssHttpMessageHandler(credentials, vssHttpRequestSettings, innerHandler);
79-
if (clientCertificate == null)
80-
innerHandler.ClientCertificateOptions = ClientCertificateOption.Automatic;
81-
return new VssConnection(adoCollectionUrl,
82-
vssHttpMessageHandler,
83-
httpMessageHandlers);
84-
}
165+
static void ParseAdoProjectUrl(string url, out string adoCollectionUrl, out string adoProjectName)
166+
{
167+
url = url.TrimEnd('/');
168+
var lastSlash = url.LastIndexOf('/');
169+
if (lastSlash < 0)
170+
{
171+
throw new InvalidOperationException($"Unable to parse Azure DevOps project URL: {url}");
172+
}
173+
174+
adoCollectionUrl = url.Substring(0, lastSlash);
175+
adoProjectName = url.Substring(lastSlash + 1);
176+
adoProjectName = WebUtility.UrlDecode(adoProjectName);
177+
}
85178

86-
bool ServerCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
87-
{
88-
if (errors == SslPolicyErrors.None)
89-
{
90-
Console.WriteLine(" SSL validation passed.");
91-
return true;
179+
static void PrintError(Exception exception, string indent = "")
180+
{
181+
var errorCode = "";
182+
if (exception is Win32Exception win32Exception)
183+
{
184+
errorCode = $" (0x{win32Exception.NativeErrorCode:x8})";
185+
}
186+
187+
Console.WriteLine($"{indent}{exception.GetType().FullName}{errorCode}: {exception.Message}");
188+
if (exception is AggregateException aggregateException)
189+
{
190+
foreach (var innerEx in aggregateException.InnerExceptions)
191+
{
192+
PrintError(innerEx, indent + " ---> ");
193+
}
194+
}
195+
else if (exception.InnerException != null)
196+
{
197+
PrintError(exception.InnerException, indent + " ---> ");
198+
}
199+
200+
//more detailed log:
201+
//if (indent == "")
202+
//{
203+
// Console.WriteLine();
204+
// Console.WriteLine(exception);
205+
//}
206+
}
92207
}
93-
94-
var hashString = certificate.GetCertHashString();
95-
Console.WriteLine($"SSL policy error(s) '{errors}' found for certificate thumbprint '{hashString}'.");
96-
Console.WriteLine(" SSL validation failed. Ignoring...");
97-
return true;
98-
}
99-
100-
101-
102-
Console.WriteLine("*** SpecSync for Azure DevOps Connection Tester ***");
103-
Console.WriteLine();
104-
105-
if (args.Length < 2)
106-
{
107-
Console.WriteLine("Usage:");
108-
Console.WriteLine(" SpecSync.ConnectionTester.exe <project-url> <pat> [<client-certificate-file>.pfx]");
109-
Console.WriteLine("OR");
110-
Console.WriteLine(" SpecSync.ConnectionTester.exe <project-url> <username> <password> [<client-certificate-file>.pfx]");
111-
return;
112-
}
113-
114-
var projectUrl = args[0];
115-
var userNameOrPat = args[1];
116-
var password = args.Length == 2 || args[2].EndsWith(".pfx", StringComparison.InvariantCultureIgnoreCase) ? "" : args[2];
117-
var clientCertificateFile = args.Last().EndsWith(".pfx", StringComparison.InvariantCultureIgnoreCase) ? args.Last() : null;
118-
119-
ParseAdoProjectUrl(projectUrl, out var collectionUrl, out var projectName);
120-
121-
if (clientCertificateFile != null)
122-
{
123-
clientCertificateFile = Path.GetFullPath(clientCertificateFile);
124-
if (!File.Exists(clientCertificateFile))
125-
throw new InvalidOperationException($"Client certificate file does not exist: {clientCertificateFile}");
126-
}
127-
128-
Console.WriteLine($"Collection URL: {collectionUrl}");
129-
Console.WriteLine($"Project Name: {projectName}");
130-
if (clientCertificateFile != null)
131-
Console.WriteLine($"Client certificate file: {clientCertificateFile}");
132-
Console.WriteLine();
133-
134-
var clientCertificate = clientCertificateFile != null ? LoadClientCertificate(clientCertificateFile) : null;
135-
136-
Console.WriteLine("Testing connection with Azure DevOps .NET API...");
137-
138-
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
139-
140-
try
141-
{
142-
var vssConnection = CreateVssConnection(new Uri(collectionUrl), new VssBasicCredential(userNameOrPat, password), clientCertificate);
143-
vssConnection.ConnectAsync().Wait();
144-
vssConnection.GetClient<ProjectHttpClient>().GetProject(projectName).Wait();
145-
Console.WriteLine("Succeeded!");
146-
}
147-
catch (Exception ex)
148-
{
149-
Console.WriteLine("Failed!");
150-
PrintError(ex);
151-
}
152-
Console.WriteLine();
153-
Console.WriteLine();
154-
Console.WriteLine("Testing connection with HttpClient...");
155-
try
156-
{
157-
var handler = new HttpClientHandler();
158-
if (clientCertificate != null)
159-
handler.ClientCertificates.Add(clientCertificate);
160-
else
161-
handler.ClientCertificateOptions = ClientCertificateOption.Automatic;
162-
163-
var httpClient = new HttpClient(handler);
164-
httpClient.BaseAddress = new Uri(collectionUrl + "/");
165-
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.UTF8.GetBytes(userNameOrPat + ":" + password)));
166-
var response = httpClient.GetAsync($"_apis/projects/{projectName}?includeHistory=False").Result;
167-
Console.WriteLine($" {response.RequestMessage?.RequestUri}");
168-
Console.WriteLine($" {(int)response.StatusCode} ({response.StatusCode})");
169-
if (response.StatusCode != HttpStatusCode.OK)
170-
Console.WriteLine("Failed! Wrong HTTP status code.");
171-
else
172-
Console.WriteLine("Succeeded!");
173-
}
174-
catch (Exception ex)
175-
{
176-
Console.WriteLine("Failed!");
177-
PrintError(ex);
178208
}
179-
Console.WriteLine();
180-
Console.WriteLine();

SpecSync.AzureDevOps.ConnectionTester.csproj

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
5-
<TargetFramework>net6.0</TargetFramework>
6-
<ImplicitUsings>enable</ImplicitUsings>
7-
<Nullable>enable</Nullable>
5+
<TargetFramework>net5.0</TargetFramework>
86
</PropertyGroup>
97

108
<ItemGroup>

0 commit comments

Comments
 (0)