Skip to content

Commit 64e6a77

Browse files
committed
Allow adding digital signatures to webhook headers
- Because webhook bodies and headers are transformed separately, Handlebars cannot be used to generate digital signatures using both the body and the header. This change allows the user to specify two callbacks to use to generate a digital signature for the webhook before it is sent.
1 parent 214fb53 commit 64e6a77

File tree

5 files changed

+102
-0
lines changed

5 files changed

+102
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// Copyright © WireMock.Net
2+
3+
using System.Threading.Tasks;
4+
using WireMock.Util;
5+
6+
namespace WireMock.Models;
7+
8+
/// <summary>
9+
/// A delegate encapsulating logic for fetching or generating a private key.
10+
/// </summary>
11+
/// <returns>An async task containing the value of the private key.</returns>
12+
public delegate Task<string> PrivateKeyCallback();
13+
14+
/// <summary>
15+
/// A delegate encapsulating logic for fetching or generating a digital signature.
16+
/// </summary>
17+
/// <param name="privateKey">The value of the private key returned by <see cref="PrivateKeyCallback"/>.</param>
18+
/// <param name="bodyData">The body data associated with the request.</param>
19+
/// <returns>An async task containing the value of the digital signature.</returns>
20+
public delegate Task<string> DigitalSignatureCallback(string privateKey, IBodyData? bodyData);
21+
22+
/// <summary>
23+
/// An interface encapsulating logic for generating a digital signature.
24+
/// </summary>
25+
public interface IDigitalSignature
26+
{
27+
/// <summary>
28+
/// The header name to use for the digital signature.
29+
/// </summary>
30+
string HeaderName { get; set; }
31+
32+
/// <summary>
33+
/// The callback used to fetch or generate the private key.
34+
/// </summary>
35+
PrivateKeyCallback PrivateKeyCallback { get; set; }
36+
37+
/// <summary>
38+
/// The callback used to fetch or generate the digital signature.
39+
/// </summary>
40+
DigitalSignatureCallback DigitalSignatureCallback { get; set; }
41+
}

src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs

+10
Original file line numberDiff line numberDiff line change
@@ -60,4 +60,14 @@ public interface IWebhookRequest
6060
/// Gets or sets the maximum random delay in milliseconds.
6161
/// </summary>
6262
int? MaximumRandomDelay { get; set; }
63+
64+
/// <summary>
65+
/// Gets or sets the value used to determine if digital signatures are generated.
66+
/// </summary>
67+
bool? UseDigitalSignatures { get; set; }
68+
69+
/// <summary>
70+
/// Gets or sets the values used generate digital signatures.
71+
/// </summary>
72+
IDigitalSignature[]? DigitalSignatures { get; set; }
6373
}

src/WireMock.Net/Http/WebhookSender.cs

+25
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,31 @@ IResponseMessage originalResponseMessage
7878
webhookRequestUrl = webhookRequest.Url;
7979
}
8080

81+
// Add digital signatures to header (if required)
82+
if (webhookRequest.UseDigitalSignatures == true)
83+
{
84+
if (webhookRequest.DigitalSignatures is not null)
85+
{
86+
foreach (var digitalSignature in webhookRequest.DigitalSignatures)
87+
{
88+
var privateKey = await digitalSignature.PrivateKeyCallback();
89+
var signature = await digitalSignature.DigitalSignatureCallback(privateKey, bodyData);
90+
91+
// headers may be null here
92+
headers ??= new Dictionary<string, WireMockList<string>>();
93+
94+
if (headers.ContainsKey(digitalSignature.HeaderName))
95+
{
96+
headers[digitalSignature.HeaderName].Add(signature);
97+
}
98+
else
99+
{
100+
headers[digitalSignature.HeaderName] = new WireMockList<string>(signature);
101+
}
102+
}
103+
}
104+
}
105+
81106
// Create RequestMessage
82107
var requestMessage = new RequestMessage(
83108
new UrlDetails(webhookRequestUrl),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Copyright © WireMock.Net
2+
3+
using System.Threading.Tasks;
4+
5+
namespace WireMock.Models;
6+
7+
/// <summary>
8+
/// A class encapsulating logic for generating a digital signature.
9+
/// </summary>
10+
public class DigitalSignature : IDigitalSignature
11+
{
12+
/// <inheritdoc/>
13+
public string HeaderName { get; set; } = "";
14+
15+
/// <inheritdoc/>
16+
public PrivateKeyCallback PrivateKeyCallback { get; set; } = async () => await Task.FromResult("");
17+
18+
/// <inheritdoc/>
19+
public DigitalSignatureCallback DigitalSignatureCallback { get; set; } = async (privateKey, bodyData) => await Task.FromResult("");
20+
}

src/WireMock.Net/Models/WebhookRequest.cs

+6
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,10 @@ public class WebhookRequest : IWebhookRequest
4040

4141
/// <inheritdoc />
4242
public int? MaximumRandomDelay { get; set; }
43+
44+
/// <inheritdoc />
45+
public bool? UseDigitalSignatures { get; set; }
46+
47+
/// <inheritdoc />
48+
public IDigitalSignature[]? DigitalSignatures { get; set; }
4349
}

0 commit comments

Comments
 (0)