Skip to content

Commit

Permalink
Allow adding digital signatures to webhook headers
Browse files Browse the repository at this point in the history
- 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.
  • Loading branch information
mathgr22 committed Oct 21, 2024
1 parent 214fb53 commit 64e6a77
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 0 deletions.
41 changes: 41 additions & 0 deletions src/WireMock.Net.Abstractions/Models/IDigitalSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright © WireMock.Net

using System.Threading.Tasks;
using WireMock.Util;

namespace WireMock.Models;

/// <summary>
/// A delegate encapsulating logic for fetching or generating a private key.
/// </summary>
/// <returns>An async task containing the value of the private key.</returns>
public delegate Task<string> PrivateKeyCallback();

/// <summary>
/// A delegate encapsulating logic for fetching or generating a digital signature.
/// </summary>
/// <param name="privateKey">The value of the private key returned by <see cref="PrivateKeyCallback"/>.</param>
/// <param name="bodyData">The body data associated with the request.</param>
/// <returns>An async task containing the value of the digital signature.</returns>
public delegate Task<string> DigitalSignatureCallback(string privateKey, IBodyData? bodyData);

/// <summary>
/// An interface encapsulating logic for generating a digital signature.
/// </summary>
public interface IDigitalSignature
{
/// <summary>
/// The header name to use for the digital signature.
/// </summary>
string HeaderName { get; set; }

/// <summary>
/// The callback used to fetch or generate the private key.
/// </summary>
PrivateKeyCallback PrivateKeyCallback { get; set; }

/// <summary>
/// The callback used to fetch or generate the digital signature.
/// </summary>
DigitalSignatureCallback DigitalSignatureCallback { get; set; }
}
10 changes: 10 additions & 0 deletions src/WireMock.Net.Abstractions/Models/IWebhookRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,14 @@ public interface IWebhookRequest
/// Gets or sets the maximum random delay in milliseconds.
/// </summary>
int? MaximumRandomDelay { get; set; }

/// <summary>
/// Gets or sets the value used to determine if digital signatures are generated.
/// </summary>
bool? UseDigitalSignatures { get; set; }

/// <summary>
/// Gets or sets the values used generate digital signatures.
/// </summary>
IDigitalSignature[]? DigitalSignatures { get; set; }
}
25 changes: 25 additions & 0 deletions src/WireMock.Net/Http/WebhookSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,31 @@ IResponseMessage originalResponseMessage
webhookRequestUrl = webhookRequest.Url;
}

// Add digital signatures to header (if required)
if (webhookRequest.UseDigitalSignatures == true)
{
if (webhookRequest.DigitalSignatures is not null)
{
foreach (var digitalSignature in webhookRequest.DigitalSignatures)
{
var privateKey = await digitalSignature.PrivateKeyCallback();
var signature = await digitalSignature.DigitalSignatureCallback(privateKey, bodyData);

// headers may be null here
headers ??= new Dictionary<string, WireMockList<string>>();

if (headers.ContainsKey(digitalSignature.HeaderName))
{
headers[digitalSignature.HeaderName].Add(signature);
}
else
{
headers[digitalSignature.HeaderName] = new WireMockList<string>(signature);
}
}
}
}

// Create RequestMessage
var requestMessage = new RequestMessage(
new UrlDetails(webhookRequestUrl),
Expand Down
20 changes: 20 additions & 0 deletions src/WireMock.Net/Models/DigitalSignature.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright © WireMock.Net

using System.Threading.Tasks;

namespace WireMock.Models;

/// <summary>
/// A class encapsulating logic for generating a digital signature.
/// </summary>
public class DigitalSignature : IDigitalSignature
{
/// <inheritdoc/>
public string HeaderName { get; set; } = "";

/// <inheritdoc/>
public PrivateKeyCallback PrivateKeyCallback { get; set; } = async () => await Task.FromResult("");

/// <inheritdoc/>
public DigitalSignatureCallback DigitalSignatureCallback { get; set; } = async (privateKey, bodyData) => await Task.FromResult("");
}
6 changes: 6 additions & 0 deletions src/WireMock.Net/Models/WebhookRequest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public class WebhookRequest : IWebhookRequest

/// <inheritdoc />
public int? MaximumRandomDelay { get; set; }

/// <inheritdoc />
public bool? UseDigitalSignatures { get; set; }

/// <inheritdoc />
public IDigitalSignature[]? DigitalSignatures { get; set; }
}

0 comments on commit 64e6a77

Please sign in to comment.