This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
@@ -18,13 +20,24 @@ public sealed class RemoteControlClient : IDisposable
|
||||
private readonly bool _ownsHttp;
|
||||
private readonly RemoteAgentOptions _options;
|
||||
private readonly Uri? _baseAddress;
|
||||
private readonly X509Certificate2? _clientCertificate;
|
||||
private readonly X509Certificate2? _serverCaCertificate;
|
||||
private bool _authenticated;
|
||||
|
||||
public RemoteControlClient(RemoteAgentOptions options, HttpClient? httpClient = null)
|
||||
{
|
||||
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||
_ownsHttp = httpClient is null;
|
||||
_http = httpClient ?? new HttpClient();
|
||||
if (httpClient is null)
|
||||
{
|
||||
_clientCertificate = LoadClientCertificate(options);
|
||||
_serverCaCertificate = LoadServerCaCertificate(options);
|
||||
_http = CreateHttpClient(_clientCertificate, _serverCaCertificate);
|
||||
}
|
||||
else
|
||||
{
|
||||
_http = httpClient;
|
||||
}
|
||||
if (Uri.TryCreate(options.AuthAddress, UriKind.Absolute, out var address))
|
||||
_baseAddress = new Uri($"{address.Scheme}://{address.Authority}/", UriKind.Absolute);
|
||||
AuthState = options.IsConfigured ? RemoteAuthState.NotConfigured : RemoteAuthState.NotConfigured;
|
||||
@@ -174,7 +187,7 @@ public sealed class RemoteControlClient : IDisposable
|
||||
using var request = new HttpRequestMessage(method, new Uri(_baseAddress, path.TrimStart('/')));
|
||||
request.Headers.TryAddWithoutValidation("X-Correlation-Id", NewCorrelationId());
|
||||
if (authenticated || method == HttpMethod.Post && path == "/v1/nodes/register")
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.Token);
|
||||
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _options.GetToken());
|
||||
if (body is not null)
|
||||
{
|
||||
var bytes = JsonSerializer.SerializeToUtf8Bytes(body, RemoteJson.Options);
|
||||
@@ -220,7 +233,7 @@ public sealed class RemoteControlClient : IDisposable
|
||||
if (!_options.IsConfigured)
|
||||
{
|
||||
AuthState = RemoteAuthState.NotConfigured;
|
||||
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "Remote authAddress, token and nodeId must all be configured.");
|
||||
throw new WxAgentException(WxAgentErrorCode.InvalidArgument, "Remote authAddress, token or tokenFile and nodeId must all be configured.");
|
||||
}
|
||||
_options.Validate();
|
||||
}
|
||||
@@ -249,11 +262,42 @@ public sealed class RemoteControlClient : IDisposable
|
||||
return new RemoteApiError("RemoteRequestFailed", "Remote request failed.", correlationId);
|
||||
}
|
||||
|
||||
private static X509Certificate2? LoadClientCertificate(RemoteAgentOptions options)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(options.ClientCertificateFile)) return null;
|
||||
return X509Certificate2.CreateFromPemFile(options.ClientCertificateFile, options.ClientCertificateKeyFile!);
|
||||
}
|
||||
|
||||
private static X509Certificate2? LoadServerCaCertificate(RemoteAgentOptions options) =>
|
||||
string.IsNullOrWhiteSpace(options.ServerCaFile) ? null : new X509Certificate2(options.ServerCaFile);
|
||||
|
||||
private static HttpClient CreateHttpClient(X509Certificate2? clientCertificate, X509Certificate2? serverCaCertificate)
|
||||
{
|
||||
var handler = new HttpClientHandler();
|
||||
if (clientCertificate is not null) handler.ClientCertificates.Add(clientCertificate);
|
||||
if (serverCaCertificate is not null)
|
||||
{
|
||||
handler.ServerCertificateCustomValidationCallback = (_, certificate, _, errors) =>
|
||||
{
|
||||
if (certificate is null || (errors & (SslPolicyErrors.RemoteCertificateNameMismatch | SslPolicyErrors.RemoteCertificateNotAvailable)) != 0)
|
||||
return false;
|
||||
using var chain = new X509Chain();
|
||||
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
|
||||
chain.ChainPolicy.CustomTrustStore.Add(serverCaCertificate);
|
||||
chain.ChainPolicy.RevocationMode = X509RevocationMode.NoCheck;
|
||||
return chain.Build(new X509Certificate2(certificate));
|
||||
};
|
||||
}
|
||||
return new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(15) };
|
||||
}
|
||||
|
||||
private static string Escape(string value) => Uri.EscapeDataString(value);
|
||||
private static string NewCorrelationId() => Guid.NewGuid().ToString("N");
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
if (_ownsHttp) _http.Dispose();
|
||||
_clientCertificate?.Dispose();
|
||||
_serverCaCertificate?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user