Skip to content

Commit 5ad2ba1

Browse files
committed
Changing the behavior of the middlewares to proceed with the pipeline
1 parent 4648aa7 commit 5ad2ba1

File tree

11 files changed

+216
-46
lines changed

11 files changed

+216
-46
lines changed

src/Deveel.Webhooks.Receiver.AspNetCore/Deveel.Webhooks.Receiver.AspNetCore.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<ItemGroup>
2525
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
2626
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
27+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="6.0.0" />
2728
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="6.0.0" />
2829
</ItemGroup>
2930

src/Deveel.Webhooks.Receiver.AspNetCore/Deveel.Webhooks.Receiver.AspNetCore.xml

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using System;
2+
3+
using Microsoft.Extensions.Logging;
4+
5+
namespace Deveel.Webhooks {
6+
static partial class LoggerExtensions {
7+
[LoggerMessage(EventId = -20222, Level = LogLevel.Warning,
8+
Message = "It was not possible to resolve any webhook receiver for the type '{WebhookType}'")]
9+
public static partial void WarnReceiverNotRegistered(this ILogger logger, Type webhookType);
10+
}
11+
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookDelegatedReceiverMiddleware.cs

Lines changed: 53 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@
1414

1515
using Microsoft.AspNetCore.Http;
1616
using Microsoft.Extensions.DependencyInjection;
17+
using Microsoft.Extensions.Logging;
18+
using Microsoft.Extensions.Logging.Abstractions;
19+
using Microsoft.Extensions.Options;
1720

1821
namespace Deveel.Webhooks {
1922
class WebhookDelegatedReceiverMiddleware<TWebhook> where TWebhook : class {
@@ -33,34 +36,65 @@ public WebhookDelegatedReceiverMiddleware(RequestDelegate next,
3336
syncHandler = handler;
3437
}
3538

39+
private WebhookReceiverOptions GetOptions(HttpContext context) {
40+
var snapshot = context?.RequestServices?.GetService<IOptionsSnapshot<WebhookReceiverOptions>>();
41+
return snapshot?.GetReceiverOptions<TWebhook>() ?? new WebhookReceiverOptions();
42+
}
43+
44+
private ILogger GetLogger(HttpContext context) {
45+
var loggerFactory = context?.RequestServices?.GetService<ILoggerFactory>();
46+
return loggerFactory?.CreateLogger<WebhookDelegatedReceiverMiddleware<TWebhook>>() ??
47+
NullLogger<WebhookDelegatedReceiverMiddleware<TWebhook>>.Instance;
48+
}
49+
3650

3751
public async Task InvokeAsync(HttpContext context) {
38-
try {
52+
var options = GetOptions(context);
53+
var logger = GetLogger(context);
54+
55+
try {
3956
var receiver = context.RequestServices.GetService<IWebhookReceiver<TWebhook>>();
40-
if (receiver == null) {
41-
await next(context);
57+
58+
WebhookReceiveResult<TWebhook>? result = null;
59+
60+
if (receiver != null) {
61+
result = await receiver.ReceiveAsync(context.Request, context.RequestAborted);
62+
63+
if (result != null && result.Value.Successful && result.Value.Webhook != null) {
64+
if (asyncHandler != null) {
65+
await asyncHandler(context, result.Value.Webhook, context.RequestAborted);
66+
} else if (syncHandler != null) {
67+
syncHandler(context, result.Value.Webhook);
68+
}
69+
}
4270
} else {
43-
var result = await receiver.ReceiveAsync(context.Request, context.RequestAborted);
44-
45-
if (result.SignatureValid != null && !result.SignatureValid.Value) {
46-
// TODO: get this from the configuration
47-
context.Response.StatusCode = 400;
48-
} else if (result.Webhook == null) {
49-
context.Response.StatusCode = 400;
50-
} else if (asyncHandler != null) {
51-
await asyncHandler(context, result.Webhook, context.RequestAborted);
52-
} else if (syncHandler != null) {
53-
syncHandler(context, result.Webhook);
54-
} else {
55-
await next(context);
71+
logger.WarnReceiverNotRegistered(typeof(TWebhook));
72+
}
73+
74+
await next.Invoke(context);
75+
76+
if (!context.Response.HasStarted && result != null) {
77+
if ((result?.SignatureValidated ?? false) && !(result?.SignatureValid ?? false)) {
78+
context.Response.StatusCode = options?.InvalidStatusCode ?? 400;
79+
} else if ((result?.Successful ?? false)) {
80+
context.Response.StatusCode = options?.ResponseStatusCode ?? 204;
5681
}
82+
83+
// TODO: should we emit anything here?
84+
await context.Response.WriteAsync("");
5785
}
58-
} catch (Exception ex) {
86+
} catch (WebhookReceiverException ex) {
5987
// TODO: log this error ...
6088

61-
context.Response.StatusCode = 500;
89+
if (!context.Response.HasStarted) {
90+
context.Response.StatusCode = options?.ErrorStatusCode ?? 500;
91+
await context.Response.WriteAsync("");
92+
}
93+
6294
// TODO: should we emit anything here?
6395
}
96+
97+
6498
}
6599
}
66-
}
100+
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookReceiveResult.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,15 @@ public WebhookReceiveResult(TWebhook? webhook, bool? signatureValid) : this() {
6161
/// </param>
6262
public static implicit operator WebhookReceiveResult<TWebhook>(TWebhook? webhook)
6363
=> new WebhookReceiveResult<TWebhook>(webhook, null);
64+
65+
/// <summary>
66+
/// Gets whether the signature of the webhook was validated.
67+
/// </summary>
68+
public bool SignatureValidated => SignatureValid.HasValue;
69+
70+
/// <summary>
71+
/// Gets whether the webhook was successfully received.
72+
/// </summary>
73+
public bool Successful => Webhook != null && (!SignatureValidated || SignatureValid == true);
6474
}
6575
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookReceiverMiddleware.cs

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -13,39 +13,71 @@
1313
// limitations under the License.
1414

1515
using Microsoft.AspNetCore.Http;
16+
using Microsoft.Extensions.Logging;
17+
using Microsoft.Extensions.Logging.Abstractions;
18+
using Microsoft.Extensions.Options;
1619

1720
namespace Deveel.Webhooks {
18-
class WebhookReceiverMiddleware<TWebhook> : IMiddleware where TWebhook : class {
19-
private readonly IEnumerable<IWebhookHandler<TWebhook>> handlers;
21+
class WebhookReceiverMiddleware<TWebhook> : IMiddleware where TWebhook : class {
22+
private readonly IEnumerable<IWebhookHandler<TWebhook>>? handlers;
2023
private readonly IWebhookReceiver<TWebhook> receiver;
24+
private readonly WebhookReceiverOptions options;
25+
private readonly ILogger logger;
2126

22-
public WebhookReceiverMiddleware(IWebhookReceiver<TWebhook> receiver, IEnumerable<IWebhookHandler<TWebhook>> handlers) {
27+
public WebhookReceiverMiddleware(
28+
IOptionsSnapshot<WebhookReceiverOptions> options,
29+
IWebhookReceiver<TWebhook> receiver,
30+
IEnumerable<IWebhookHandler<TWebhook>>? handlers = null,
31+
ILogger<WebhookReceiverMiddleware<TWebhook>>? logger = null) {
32+
this.options = options.GetReceiverOptions<TWebhook>();
2333
this.receiver = receiver;
2434
this.handlers = handlers;
35+
this.logger = logger ?? NullLogger<WebhookReceiverMiddleware<TWebhook>>.Instance;
2536
}
2637

38+
private int SuccessStatusCode => options.ResponseStatusCode ?? 200;
39+
40+
private int FailureStatusCode => options.ErrorStatusCode ?? 500;
41+
42+
private int InvalidStatusCode => options.InvalidStatusCode ?? 400;
43+
2744
public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
2845
try {
2946
var result = await receiver.ReceiveAsync(context.Request, context.RequestAborted);
3047

31-
if (result.SignatureValid != null && !result.SignatureValid.Value) {
32-
// TODO: get this from the configuration
33-
context.Response.StatusCode = 400;
34-
} else if (result.Webhook == null) {
35-
context.Response.StatusCode = 400;
36-
} else if (handlers != null) {
48+
if (handlers != null && result.Successful && result.Webhook != null) {
3749
foreach (var handler in handlers) {
3850
await handler.HandleAsync(result.Webhook, context.RequestAborted);
3951
}
40-
} else {
41-
await next(context);
4252
}
43-
} catch (Exception ex) {
53+
54+
await next.Invoke(context);
55+
56+
if (!context.Response.HasStarted) {
57+
if (result.Successful) {
58+
context.Response.StatusCode = SuccessStatusCode;
59+
} else if (result.SignatureValidated && !result.SignatureValid.Value) {
60+
context.Response.StatusCode = InvalidStatusCode;
61+
} else {
62+
context.Response.StatusCode = FailureStatusCode;
63+
}
64+
65+
// TODO: should we emit anything here?
66+
await context.Response.WriteAsync("");
67+
}
68+
} catch (WebhookReceiverException ex) {
4469
// TODO: log this error ...
4570

46-
context.Response.StatusCode = 500;
71+
if (!context.Response.HasStarted) {
72+
context.Response.StatusCode = FailureStatusCode;
73+
74+
// TODO: should we emit anything here?
75+
await context.Response.WriteAsync("");
76+
}
77+
4778
// TODO: should we emit anything here?
4879
}
80+
4981
}
5082
}
5183
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookReceiverOptions.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ public class WebhookReceiverOptions {
2828
/// <summary>
2929
/// Gets or sets the options for the signature verification.
3030
/// </summary>
31-
public WebhookSignatureOptions Signature { get; set; } = new WebhookSignatureOptions();
31+
public WebhookSignatureOptions? Signature { get; set; } = new WebhookSignatureOptions();
3232

3333
/// <summary>
3434
/// Gets or sets the HTTP status code to return when the webhook
3535
/// processing is successful (<c>201</c> by default).
3636
/// </summary>
37-
public int? ResponseStatusCode { get; set; } = 201;
37+
public int? ResponseStatusCode { get; set; } = 204;
3838

3939
/// <summary>
4040
/// Gets or sets the HTTP status code to return when the webhook
@@ -47,5 +47,11 @@ public class WebhookReceiverOptions {
4747
/// from the sender is invalid (<c>400</c> by default).
4848
/// </summary>
4949
public int? InvalidStatusCode { get; set; } = 400;
50+
51+
/// <summary>
52+
/// Gets or sets the options for the verification of the webhook
53+
/// requests by the sender.
54+
/// </summary>
55+
public WebhookVerificationOptions? Verification { get; set; } = new WebhookVerificationOptions();
5056
}
5157
}

src/Deveel.Webhooks.Receiver.AspNetCore/Webhooks/WebhookRequestVerfierMiddleware.cs

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,49 @@
1616
using System.Threading.Tasks;
1717

1818
using Microsoft.AspNetCore.Http;
19+
using Microsoft.Extensions.Logging;
20+
using Microsoft.Extensions.Logging.Abstractions;
21+
using Microsoft.Extensions.Options;
1922

2023
namespace Deveel.Webhooks {
2124
class WebhookRequestVerfierMiddleware<TWebhook> : IMiddleware where TWebhook : class {
22-
public Task InvokeAsync(HttpContext context, RequestDelegate next) => throw new NotImplementedException();
25+
private readonly WebhookReceiverOptions options;
26+
private readonly IWebhookRequestVerifier<TWebhook>? requestVerifier;
27+
private readonly ILogger logger;
28+
29+
public WebhookRequestVerfierMiddleware(
30+
IOptionsSnapshot<WebhookReceiverOptions> options,
31+
IWebhookRequestVerifier<TWebhook>? requestVerifier = null,
32+
ILogger<WebhookRequestVerfierMiddleware<TWebhook>>? logger = null) {
33+
this.options = options.GetReceiverOptions<TWebhook>();
34+
this.requestVerifier = requestVerifier;
35+
this.logger = logger ?? NullLogger<WebhookRequestVerfierMiddleware<TWebhook>>.Instance;
36+
}
37+
38+
public async Task InvokeAsync(HttpContext context, RequestDelegate next) {
39+
try {
40+
WebhookVerificationResult? result = null;
41+
42+
if (requestVerifier != null) {
43+
result = await requestVerifier.VerifyRequestAsync(context.Request, context.RequestAborted);
44+
}
45+
46+
await next.Invoke(context);
47+
48+
if (result != null && !context.Response.HasStarted) {
49+
if (result?.IsVerified ?? false) {
50+
51+
} else {
52+
53+
}
54+
}
55+
} catch (WebhookReceiverException ex) {
56+
if (!context.Response.HasStarted) {
57+
58+
}
59+
}
60+
61+
62+
}
2363
}
2464
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
using System;
2+
3+
namespace Deveel.Webhooks {
4+
/// <summary>
5+
/// Provides a set of options to configure the behavior of the
6+
/// verification process of a webhook request.
7+
/// </summary>
8+
public class WebhookVerificationOptions {
9+
/// <summary>
10+
/// Gets or sets the HTTP status code to return when the request
11+
/// is authorized (<c>200</c> by default).
12+
/// </summary>
13+
public int? SuccessStatusCode { get; set; } = 200;
14+
15+
/// <summary>
16+
/// Gets or sets the HTTP status code to return when the request
17+
/// fails due to an internal error (<c>500</c> by default).
18+
/// </summary>
19+
public int? FailureStatusCode { get; set; } = 500;
20+
21+
/// <summary>
22+
/// Gets or sets the HTTP status code to return when the request
23+
/// is not authorized (<c>403</c> by default).
24+
/// </summary>
25+
public int? NotAuthorizedStatusCode { get; set; } = 403;
26+
}
27+
}

test/Deveel.Webhooks.Receiver.TestApi/Program.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public static void Main(string[] args) {
1111
builder.Services.AddLogging();
1212

1313
// Add services to the container.
14-
builder.Services.AddAuthorization();
14+
// builder.Services.AddAuthorization();
1515
builder.Services
1616
.AddWebhooks<TestWebhook>()
1717
.UseNewtonsoftJsonParser()
@@ -32,11 +32,13 @@ public static void Main(string[] args) {
3232

3333
var app = builder.Build();
3434

35-
// Configure the HTTP request pipeline.
35+
// app.UseDeveloperExceptionPage();
3636

37-
app.UseHttpsRedirection();
37+
// Configure the HTTP request pipeline.
3838

39-
app.UseAuthorization();
39+
// app.UseHttpsRedirection();
40+
41+
// app.UseAuthorization();
4042

4143
app.UseWebhookReceiver<TestWebhook>("/webhook");
4244
app.UseWebhookReceiver<TestWebhook>("/webhook/handled", (context, webhook) => {

0 commit comments

Comments
 (0)