1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

[PM-17562] Add support for Auth on Webhook integration requests (#5970)

* [PM-17562] Update documentation for event integrations

* Fix SonarQube suggestion, bring ASB event listener in line with integration listener

* Apply suggestions from code review

Co-authored-by: Matt Bishop <mbishop@bitwarden.com>

* Updates to README - PR fixes, additional context, tense alignment

* Add links to different sections; remove inline code formatting in favor of single bacticks for JSON

* [PM-17562] Add aupport for Auth on Webhook integration requests

* Repsond to PR feedback - move optional params to end, add tests for optional cases

---------

Co-authored-by: Matt Bishop <mbishop@bitwarden.com>
This commit is contained in:
Brant DeBow
2025-06-26 09:19:49 -04:00
committed by GitHub
parent 7fd1ccb7a2
commit b418b07f26
13 changed files with 160 additions and 29 deletions

View File

@ -14,7 +14,7 @@ public class IntegrationMessageTests
{
var message = new IntegrationMessage<WebhookIntegrationConfigurationDetails>
{
Configuration = new WebhookIntegrationConfigurationDetails("https://localhost"),
Configuration = new WebhookIntegrationConfigurationDetails("https://localhost", "Bearer", "AUTH-TOKEN"),
MessageId = _messageId,
RetryCount = 2,
RenderedTemplate = string.Empty,
@ -34,7 +34,7 @@ public class IntegrationMessageTests
{
var message = new IntegrationMessage<WebhookIntegrationConfigurationDetails>
{
Configuration = new WebhookIntegrationConfigurationDetails("https://localhost"),
Configuration = new WebhookIntegrationConfigurationDetails("https://localhost", "Bearer", "AUTH-TOKEN"),
MessageId = _messageId,
RenderedTemplate = "This is the message",
IntegrationType = IntegrationType.Webhook,

View File

@ -62,7 +62,7 @@ public class EventIntegrationHandlerTests
{
var config = Substitute.For<OrganizationIntegrationConfigurationDetails>();
config.Configuration = null;
config.IntegrationConfiguration = JsonSerializer.Serialize(new { url = _url });
config.IntegrationConfiguration = JsonSerializer.Serialize(new { Url = _url });
config.Template = template;
return [config];
@ -72,11 +72,11 @@ public class EventIntegrationHandlerTests
{
var config = Substitute.For<OrganizationIntegrationConfigurationDetails>();
config.Configuration = null;
config.IntegrationConfiguration = JsonSerializer.Serialize(new { url = _url });
config.IntegrationConfiguration = JsonSerializer.Serialize(new { Url = _url });
config.Template = template;
var config2 = Substitute.For<OrganizationIntegrationConfigurationDetails>();
config2.Configuration = null;
config2.IntegrationConfiguration = JsonSerializer.Serialize(new { url = _url2 });
config2.IntegrationConfiguration = JsonSerializer.Serialize(new { Url = _url2 });
config2.Template = template;
return [config, config2];

View File

@ -14,7 +14,7 @@ public class IntegrationHandlerTests
var sut = new TestIntegrationHandler();
var expected = new IntegrationMessage<WebhookIntegrationConfigurationDetails>()
{
Configuration = new WebhookIntegrationConfigurationDetails("https://localhost"),
Configuration = new WebhookIntegrationConfigurationDetails("https://localhost", "Bearer", "AUTH-TOKEN"),
MessageId = "TestMessageId",
IntegrationType = IntegrationType.Webhook,
RenderedTemplate = "Template",

View File

@ -1,4 +1,5 @@
using System.Net;
using System.Net.Http.Headers;
using Bit.Core.AdminConsole.Models.Data.EventIntegrations;
using Bit.Core.Services;
using Bit.Test.Common.AutoFixture;
@ -16,6 +17,8 @@ public class WebhookIntegrationHandlerTests
{
private readonly MockedHttpMessageHandler _handler;
private readonly HttpClient _httpClient;
private const string _scheme = "Bearer";
private const string _token = "AUTH_TOKEN";
private const string _webhookUrl = "http://localhost/test/event";
public WebhookIntegrationHandlerTests()
@ -39,7 +42,7 @@ public class WebhookIntegrationHandlerTests
}
[Theory, BitAutoData]
public async Task HandleAsync_SuccessfulRequest_ReturnsSuccess(IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
public async Task HandleAsync_SuccessfulRequestWithoutAuth_ReturnsSuccess(IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
{
var sutProvider = GetSutProvider();
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl);
@ -59,6 +62,33 @@ public class WebhookIntegrationHandlerTests
var returned = await request.Content.ReadAsStringAsync();
Assert.Equal(HttpMethod.Post, request.Method);
Assert.Null(request.Headers.Authorization);
Assert.Equal(_webhookUrl, request.RequestUri.ToString());
AssertHelper.AssertPropertyEqual(message.RenderedTemplate, returned);
}
[Theory, BitAutoData]
public async Task HandleAsync_SuccessfulRequestWithAuthorizationHeader_ReturnsSuccess(IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
{
var sutProvider = GetSutProvider();
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl, _scheme, _token);
var result = await sutProvider.Sut.HandleAsync(message);
Assert.True(result.Success);
Assert.Equal(result.Message, message);
sutProvider.GetDependency<IHttpClientFactory>().Received(1).CreateClient(
Arg.Is(AssertHelper.AssertPropertyEqual(WebhookIntegrationHandler.HttpClientName))
);
Assert.Single(_handler.CapturedRequests);
var request = _handler.CapturedRequests[0];
Assert.NotNull(request);
var returned = await request.Content.ReadAsStringAsync();
Assert.Equal(HttpMethod.Post, request.Method);
Assert.Equal(new AuthenticationHeaderValue(_scheme, _token), request.Headers.Authorization);
Assert.Equal(_webhookUrl, request.RequestUri.ToString());
AssertHelper.AssertPropertyEqual(message.RenderedTemplate, returned);
}
@ -71,7 +101,7 @@ public class WebhookIntegrationHandlerTests
var retryAfter = now.AddSeconds(60);
sutProvider.GetDependency<FakeTimeProvider>().SetUtcNow(now);
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl);
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl, _scheme, _token);
_handler.Fallback
.WithStatusCode(HttpStatusCode.TooManyRequests)
@ -94,7 +124,7 @@ public class WebhookIntegrationHandlerTests
var sutProvider = GetSutProvider();
var now = new DateTime(2014, 3, 2, 1, 0, 0, DateTimeKind.Utc);
var retryAfter = now.AddSeconds(60);
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl);
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl, _scheme, _token);
_handler.Fallback
.WithStatusCode(HttpStatusCode.TooManyRequests)
@ -115,7 +145,7 @@ public class WebhookIntegrationHandlerTests
public async Task HandleAsync_InternalServerError_ReturnsFailureSetsRetryable(IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
{
var sutProvider = GetSutProvider();
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl);
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl, _scheme, _token);
_handler.Fallback
.WithStatusCode(HttpStatusCode.InternalServerError)
@ -134,7 +164,7 @@ public class WebhookIntegrationHandlerTests
public async Task HandleAsync_UnexpectedRedirect_ReturnsFailureNotRetryable(IntegrationMessage<WebhookIntegrationConfigurationDetails> message)
{
var sutProvider = GetSutProvider();
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl);
message.Configuration = new WebhookIntegrationConfigurationDetails(_webhookUrl, _scheme, _token);
_handler.Fallback
.WithStatusCode(HttpStatusCode.TemporaryRedirect)