mirror of
https://github.com/Cleanuparr/Cleanuparr.git
synced 2026-11-01 12:03:34 -05:00
added http digest auth to download clients
This commit is contained in:
1 parent
9f44e0e33f
commit
c16a6753f4
37 files changed
+5376
-66
No files matched your search
+27
@@ -0,0 +1,27 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Text;
|
||||
|
||||
namespace Cleanuparr.Infrastructure.Http.DynamicHttpClientSystem;
|
||||
|
||||
/// <summary>
|
||||
/// Sends Basic auth up front for qBittorrent, Deluge and SABnzbd behind a reverse proxy.
|
||||
/// Skips requests that already carry an Authorization header.
|
||||
/// </summary>
|
||||
public sealed class BasicAuthHttpMessageHandler : DelegatingHandler
|
||||
{
|
||||
private readonly string _headerValue;
|
||||
|
||||
public BasicAuthHttpMessageHandler(NetworkCredential credential)
|
||||
{
|
||||
string raw = $"{credential.UserName}:{credential.Password}";
|
||||
_headerValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(raw));
|
||||
}
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
request.Headers.Authorization ??= new AuthenticationHeaderValue("Basic", _headerValue);
|
||||
|
||||
return base.SendAsync(request, cancellationToken);
|
||||
}
|
||||
}
|
||||
+54
-40
@@ -1,4 +1,7 @@
|
||||
using System.Net;
|
||||
using System.Net.Security;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Cleanuparr.Domain.Enums;
|
||||
using Cleanuparr.Infrastructure.Services;
|
||||
using Cleanuparr.Shared.Helpers;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
@@ -67,51 +70,62 @@ public class DynamicHttpClientConfiguration : IConfigureNamedOptions<HttpClientF
|
||||
{
|
||||
using var scope = _scopeFactory.CreateScope();
|
||||
var certValidationService = scope.ServiceProvider.GetRequiredService<CertificateValidationService>();
|
||||
|
||||
switch (config.Type)
|
||||
|
||||
bool ByPassValidation(HttpRequestMessage sender, X509Certificate2? certificate, X509Chain? chain, SslPolicyErrors policy) =>
|
||||
certValidationService.ShouldByPassValidationError(config.CertificateValidationType, sender, certificate, chain, policy);
|
||||
|
||||
HttpClientHandler handler = config.Type switch
|
||||
{
|
||||
case HttpClientType.WithRetry:
|
||||
builder.PrimaryHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, certificate, chain, policy) =>
|
||||
certValidationService.ShouldByPassValidationError(config.CertificateValidationType, sender, certificate, chain, policy)
|
||||
};
|
||||
break;
|
||||
HttpClientType.Deluge => new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = config.AllowAutoRedirect,
|
||||
UseCookies = true,
|
||||
CookieContainer = new CookieContainer(),
|
||||
AutomaticDecompression = config.AutomaticDecompression,
|
||||
ServerCertificateCustomValidationCallback = ByPassValidation,
|
||||
},
|
||||
|
||||
case HttpClientType.Deluge:
|
||||
builder.PrimaryHandler = new HttpClientHandler
|
||||
{
|
||||
AllowAutoRedirect = config.AllowAutoRedirect,
|
||||
UseCookies = true,
|
||||
CookieContainer = new CookieContainer(),
|
||||
AutomaticDecompression = config.AutomaticDecompression,
|
||||
ServerCertificateCustomValidationCallback = (sender, certificate, chain, policy) =>
|
||||
certValidationService.ShouldByPassValidationError(config.CertificateValidationType, sender, certificate, chain, policy),
|
||||
};
|
||||
break;
|
||||
// uTorrent's WebUI requires the GUID cookie+token pair set manually by UTorrentHttpService
|
||||
// UseCookies=false prevents .NET's CookieContainer from injecting a competing Cookie header.
|
||||
HttpClientType.UTorrent => new HttpClientHandler
|
||||
{
|
||||
UseCookies = false,
|
||||
ServerCertificateCustomValidationCallback = ByPassValidation,
|
||||
},
|
||||
|
||||
case HttpClientType.UTorrent:
|
||||
// uTorrent's WebUI requires the GUID cookie+token pair set manually by UTorrentHttpService
|
||||
// UseCookies=false prevents .NET's CookieContainer from injecting a competing Cookie header.
|
||||
builder.PrimaryHandler = new HttpClientHandler
|
||||
{
|
||||
UseCookies = false,
|
||||
ServerCertificateCustomValidationCallback = (sender, certificate, chain, policy) =>
|
||||
certValidationService.ShouldByPassValidationError(config.CertificateValidationType, sender, certificate, chain, policy),
|
||||
};
|
||||
break;
|
||||
// WithRetry and Default use the same handler shape.
|
||||
_ => new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = ByPassValidation,
|
||||
},
|
||||
};
|
||||
|
||||
case HttpClientType.Default:
|
||||
default:
|
||||
// Use default handler with certificate validation
|
||||
var defaultHandler = new HttpClientHandler
|
||||
{
|
||||
ServerCertificateCustomValidationCallback = (sender, certificate, chain, policy) =>
|
||||
certValidationService.ShouldByPassValidationError(config.CertificateValidationType, sender, certificate, chain, policy)
|
||||
};
|
||||
builder.PrimaryHandler = defaultHandler;
|
||||
break;
|
||||
ApplyHttpAuth(builder.AdditionalHandlers, handler, config);
|
||||
|
||||
builder.PrimaryHandler = handler;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Digest goes on <see cref="HttpClientHandler.Credentials"/>, Basic on a delegating handler.
|
||||
/// </summary>
|
||||
internal static void ApplyHttpAuth(ICollection<DelegatingHandler> additionalHandlers, HttpClientHandler handler, HttpClientConfig config)
|
||||
{
|
||||
if (config.HttpAuthCredential is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (config.HttpAuthType == HttpAuthType.Digest)
|
||||
{
|
||||
Uri baseUri = config.BaseUri ?? throw new InvalidOperationException("Digest authentication requires a base URI");
|
||||
|
||||
// The "Digest" key blocks a fallback to NTLM/Negotiate for this authority.
|
||||
handler.Credentials = new CredentialCache { { baseUri, "Digest", config.HttpAuthCredential } };
|
||||
handler.PreAuthenticate = true;
|
||||
return;
|
||||
}
|
||||
|
||||
additionalHandlers.Add(new BasicAuthHttpMessageHandler(config.HttpAuthCredential));
|
||||
}
|
||||
|
||||
private void AddRetryPolicy(HttpMessageHandlerBuilder builder, RetryConfig retryConfig)
|
||||
|
||||
+14
-2
@@ -82,7 +82,16 @@ public class DynamicHttpClientFactory : IDynamicHttpClientFactory
|
||||
RegisterConfiguration(clientName, config);
|
||||
}
|
||||
|
||||
public void RegisterDownloadClient(string clientName, int timeout, HttpClientType clientType, RetryConfig retryConfig, CertificateValidationType certificateType, bool sendUserAgent)
|
||||
public void RegisterDownloadClient(
|
||||
string clientName,
|
||||
int timeout,
|
||||
HttpClientType clientType,
|
||||
RetryConfig retryConfig,
|
||||
CertificateValidationType certificateType,
|
||||
bool sendUserAgent,
|
||||
HttpAuthType httpAuthType = HttpAuthType.Basic,
|
||||
NetworkCredential? httpAuthCredential = null,
|
||||
Uri? baseUri = null)
|
||||
{
|
||||
HttpClientConfig config = new()
|
||||
{
|
||||
@@ -91,7 +100,10 @@ public class DynamicHttpClientFactory : IDynamicHttpClientFactory
|
||||
Type = clientType,
|
||||
RetryConfig = retryConfig,
|
||||
CertificateValidationType = certificateType,
|
||||
SendUserAgent = sendUserAgent
|
||||
SendUserAgent = sendUserAgent,
|
||||
HttpAuthType = httpAuthType,
|
||||
HttpAuthCredential = httpAuthCredential,
|
||||
BaseUri = baseUri,
|
||||
};
|
||||
|
||||
// Configure Deluge-specific settings if needed
|
||||
|
||||
+15
@@ -23,4 +23,19 @@ public class HttpClientConfig
|
||||
/// Whether requests from this client carry a User-Agent header.
|
||||
/// </summary>
|
||||
public bool SendUserAgent { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// HTTP authentication scheme to answer a reverse proxy's challenge with.
|
||||
/// </summary>
|
||||
public HttpAuthType HttpAuthType { get; set; } = HttpAuthType.Basic;
|
||||
|
||||
/// <summary>
|
||||
/// Credential to send as HTTP auth, or null when this client has none configured.
|
||||
/// </summary>
|
||||
public NetworkCredential? HttpAuthCredential { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Scheme, host and port of the client's URL, used as the Digest <see cref="CredentialCache"/> key.
|
||||
/// </summary>
|
||||
public Uri? BaseUri { get; set; }
|
||||
}
|
||||
+11
-1
@@ -1,3 +1,4 @@
|
||||
using System.Net;
|
||||
using Cleanuparr.Domain.Enums;
|
||||
using Cleanuparr.Persistence.Models.Configuration.General;
|
||||
|
||||
@@ -36,7 +37,16 @@ public interface IDynamicHttpClientFactory
|
||||
/// <summary>
|
||||
/// Registers a configuration for a download client
|
||||
/// </summary>
|
||||
void RegisterDownloadClient(string clientName, int timeout, HttpClientType clientType, RetryConfig retryConfig, CertificateValidationType certificateType, bool sendUserAgent);
|
||||
void RegisterDownloadClient(
|
||||
string clientName,
|
||||
int timeout,
|
||||
HttpClientType clientType,
|
||||
RetryConfig retryConfig,
|
||||
CertificateValidationType certificateType,
|
||||
bool sendUserAgent,
|
||||
HttpAuthType httpAuthType = HttpAuthType.Basic,
|
||||
NetworkCredential? httpAuthCredential = null,
|
||||
Uri? baseUri = null);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a client that opts out of every handler setting and only carries the User-Agent.
|
||||
|
||||
Reference in new issue
Block a user