Skip to content

Commit b0a64b5

Browse files
authored
Merge pull request #16 from simonabler/master
[feature] Allow custom RestClientOptions in DigestAuthenticator to configure internal RestClient
2 parents 4618d20 + 9b216c7 commit b0a64b5

6 files changed

Lines changed: 93 additions & 11 deletions

File tree

RELEASES.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@
1818

1919
## v2.0.0
2020

21-
- Changes the compatibility to the RestSharp version `111.2.0` or greater.
21+
- Changes the compatibility to the RestSharp version `111.2.0` or greater.
22+
23+
## v2.0.1
24+
25+
- Add ability to change client options from digest RestClient object

src/DigestAuthenticator/DigestAuthenticator.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public class DigestAuthenticator : IAuthenticator
1616

1717
private readonly string _username;
1818
private readonly TimeSpan _timeout;
19+
private readonly RestClientOptions? _handshakeClientOptions;
1920

2021
/// <summary>
2122
/// Creates a new instance of <see cref="DigestAuthenticator" /> class.
@@ -24,7 +25,7 @@ public class DigestAuthenticator : IAuthenticator
2425
/// <param name="password">The password.</param>
2526
/// <param name="logger">The optional logger.</param>
2627
/// <param name="timeout">The request timeout.</param>
27-
public DigestAuthenticator(string username, string password, int timeout = DEFAULT_TIMEOUT, ILogger? logger = null)
28+
public DigestAuthenticator(string username, string password, int timeout = DEFAULT_TIMEOUT, ILogger? logger = null, RestClientOptions? restClientOptions=null)
2829
{
2930
if (string.IsNullOrWhiteSpace(username))
3031
{
@@ -45,15 +46,16 @@ public DigestAuthenticator(string username, string password, int timeout = DEFAU
4546
_password = password;
4647
_timeout = TimeSpan.FromMilliseconds(timeout);
4748
_logger = logger ?? NullLogger.Instance;
49+
_handshakeClientOptions = restClientOptions;
4850
}
4951

5052
/// <inheritdoc cref="IAuthenticator" />
5153
public async ValueTask Authenticate(IRestClient client, RestRequest request)
5254
{
5355
_logger.LogDebug("Initiate Digest authentication");
5456
var uri = client.BuildUri(request);
55-
var manager = new DigestAuthenticatorManager(client.BuildUri(new RestRequest()), _username, _password, _timeout, _logger);
56-
await manager.GetDigestAuthHeader(uri.PathAndQuery, request.Method,client.Options.Proxy).ConfigureAwait(false);
57+
var manager = new DigestAuthenticatorManager(client.BuildUri(new RestRequest()), _username, _password, _timeout, _handshakeClientOptions, _logger);
58+
await manager.GetDigestAuthHeader(uri.PathAndQuery, request.Method, client.Options.Proxy).ConfigureAwait(false);
5759
var digestHeader = manager.GetDigestHeader(uri.PathAndQuery, request.Method);
5860
request.AddOrUpdateHeader("Connection", "Keep-Alive");
5961
request.AddOrUpdateHeader(KnownHeaders.Authorization, digestHeader);

src/DigestAuthenticator/DigestAuthenticatorManager.cs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Globalization;
33
using System.Linq;
44
using System.Net;
5+
using System.Net.Security;
56
using System.Reflection;
67
using System.Security.Authentication;
78
using System.Security.Cryptography;
@@ -47,6 +48,7 @@ internal class DigestAuthenticatorManager
4748
/// </summary>
4849
private string? _realm;
4950

51+
private readonly RestClientOptions? _handshakeClientOptions;
5052
/// <summary>
5153
/// The opaque that is returned by the first digest request (without the data).
5254
/// </summary>
@@ -65,7 +67,7 @@ static DigestAuthenticatorManager()
6567
/// <param name="password">The password.</param>
6668
/// <param name="timeout">The timeout.</param>
6769
/// <param name="logger"></param>
68-
public DigestAuthenticatorManager(Uri host, string username, string password, TimeSpan timeout, ILogger logger)
70+
public DigestAuthenticatorManager(Uri host, string username, string password, TimeSpan timeout, RestClientOptions? handshakeClientOptions, ILogger logger)
6971
{
7072
if (string.IsNullOrWhiteSpace(username))
7173
{
@@ -87,6 +89,7 @@ public DigestAuthenticatorManager(Uri host, string username, string password, Ti
8789
_password = password;
8890
_timeout = timeout;
8991
_logger = logger;
92+
_handshakeClientOptions = handshakeClientOptions;
9093
}
9194

9295
/// <summary>
@@ -98,7 +101,7 @@ public DigestAuthenticatorManager(Uri host, string username, string password, Ti
98101
public async Task GetDigestAuthHeader(
99102
string path,
100103
Method method,
101-
IWebProxy? proxy = default)
104+
IWebProxy? proxy = null)
102105
{
103106
_logger.LogDebug("Initiating GetDigestAuthHeader");
104107
var uri = new Uri(_host, path);
@@ -108,11 +111,19 @@ public async Task GetDigestAuthHeader(
108111
request.AddOrUpdateHeader("User-Agent", $"RestSharp.Authenticators.Digest/{_assemblyVersion}");
109112
request.AddOrUpdateHeader("Accept-Encoding", "gzip, deflate, br");
110113
request.Timeout = _timeout;
111-
using var client = new RestClient(new RestClientOptions()
114+
115+
RestClient client;
116+
if (_handshakeClientOptions != null)
117+
{
118+
client = new RestClient(_handshakeClientOptions);
119+
}
120+
else
112121
{
113-
Proxy = proxy
114-
});
122+
client = new RestClient(new RestClientOptions() { Proxy = proxy });
123+
}
124+
115125
var response = await client.ExecuteAsync(request).ConfigureAwait(false);
126+
client.Dispose();
116127
GetDigestDataFromFailResponse(response);
117128
_logger.LogDebug("GetDigestAuthHeader completed");
118129
}

test/DigestAuthenticator.Tests/DigestIntegrationTest.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,4 +37,31 @@ public async Task Given_ADigestAuthEndpoint_When_ITryToGetInfo_Then_TheAuthMustB
3737
response.StatusCode.Should().Be(HttpStatusCode.OK);
3838
loggerMock.ReceivedWithAnyArgs().LogDebug("NONONO");
3939
}
40+
41+
42+
[Fact]
43+
public async Task Given_ADigestAuthEndpoint_When_ITryToInjectOwnClient_Then_TheAuthMustBeResolved()
44+
{
45+
bool proxyCalled = false;
46+
47+
var loggerMock = Substitute.For<ILogger>();
48+
loggerMock.BeginScope("DigestServerStub");
49+
50+
var request = new RestRequest("values");
51+
request.AddHeader("Content-Type", "application/json");
52+
53+
RestClientOptions options = new RestClientOptions()
54+
{
55+
Proxy = new TestProxy(() => proxyCalled = true)
56+
};
57+
58+
var client = _fixture.CreateInjectedOptionClient(loggerMock, options);
59+
var response = await client.ExecuteAsync(request);
60+
61+
response.StatusCode.Should().Be(HttpStatusCode.OK);
62+
loggerMock.ReceivedWithAnyArgs().LogDebug("NONONO");
63+
64+
Assert.True(proxyCalled, "Injected RestClientOptions.Proxy should be used in digest handshake.");
65+
66+
}
4067
}

test/DigestAuthenticator.Tests/Fixtures/DigestServerStub.cs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ public class DigestServerStub : IAsyncDisposable
1515
{
1616
private readonly CancellationTokenSource _cancellationTokenSource;
1717
private readonly Task _serverTask;
18-
18+
1919
private const string REALM = "test-realm";
2020
private const string USERNAME = "test-user";
2121
private const string PASSWORD = "test-password";
@@ -26,7 +26,7 @@ public DigestServerStub()
2626
var nonce = GenerateNonce();
2727

2828
_cancellationTokenSource = new CancellationTokenSource();
29-
29+
3030
_serverTask = StartServer(REALM, USERNAME, PASSWORD, nonce, PORT);
3131
Console.WriteLine($"Server started! port: {PORT}.");
3232
}
@@ -41,6 +41,17 @@ public IRestClient CreateClient(ILogger logger)
4141
return new RestClient(restOptions);
4242
}
4343

44+
public IRestClient CreateInjectedOptionClient(ILogger logger, RestClientOptions clientOptions )
45+
{
46+
47+
var restOptions = new RestClientOptions($"http://localhost:{PORT}")
48+
{
49+
Authenticator = new DigestAuthenticator(USERNAME, PASSWORD, logger: logger, restClientOptions: clientOptions)
50+
};
51+
52+
return new RestClient(restOptions);
53+
}
54+
4455
public async ValueTask DisposeAsync()
4556
{
4657
GC.SuppressFinalize(this);
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Net;
5+
using System.Text;
6+
using System.Threading.Tasks;
7+
8+
namespace RestSharp.Authenticators.Digest.Tests.Fixtures;
9+
internal class TestProxy : IWebProxy
10+
{
11+
private readonly Action _onProxyCalled;
12+
13+
public TestProxy(Action onProxyCalled)
14+
{
15+
_onProxyCalled = onProxyCalled;
16+
}
17+
18+
public Uri GetProxy(Uri destination)
19+
{
20+
_onProxyCalled();
21+
return destination;
22+
}
23+
24+
public bool IsBypassed(Uri host) => false;
25+
26+
public ICredentials? Credentials { get; set; }
27+
}

0 commit comments

Comments
 (0)