mirror of
https://github.com/bitwarden/server.git
synced 2025-04-05 05:00:19 -05:00
Refactored Slack and Webhook integrations to pull configurations dynamically from a new Repository
This commit is contained in:
parent
dc342713a6
commit
862f1821c8
7
src/Core/AdminConsole/Enums/IntegrationType.cs
Normal file
7
src/Core/AdminConsole/Enums/IntegrationType.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Enums;
|
||||
|
||||
public enum IntegrationType : int
|
||||
{
|
||||
Slack = 1,
|
||||
Webhook = 2,
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
|
||||
public class IntegrationConfiguration<T>
|
||||
{
|
||||
public T Configuration { get; set; }
|
||||
public string Template { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,8 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
|
||||
public class SlackConfiguration
|
||||
{
|
||||
public string Token { get; set; } = string.Empty;
|
||||
public List<string> Channels { get; set; } = new();
|
||||
public List<string> UserEmails { get; set; } = new();
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
namespace Bit.Core.Models.Data.Integrations;
|
||||
|
||||
public class WebhookConfiguration
|
||||
{
|
||||
public string Url { get; set; } = string.Empty;
|
||||
public string ApiKey { get; set; } = string.Empty;
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Integrations;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.Repositories;
|
||||
|
||||
public interface IOrganizationIntegrationConfigurationRepository
|
||||
{
|
||||
Task<IntegrationConfiguration<T>?> GetConfigurationAsync<T>(Guid organizationId, IntegrationType integrationType, EventType eventType);
|
||||
Task<IEnumerable<IntegrationConfiguration<T>>> GetAllConfigurationsAsync<T>(Guid organizationId);
|
||||
Task AddConfigurationAsync<T>(Guid organizationId, IntegrationType integrationType, EventType eventType, IntegrationConfiguration<T> configuration);
|
||||
Task UpdateConfigurationAsync<T>(IntegrationConfiguration<T> configuration);
|
||||
Task DeleteConfigurationAsync(Guid id);
|
||||
}
|
@ -0,0 +1,65 @@
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data.Integrations;
|
||||
using Bit.Core.Settings;
|
||||
|
||||
namespace Bit.Core.Repositories;
|
||||
|
||||
#nullable enable
|
||||
|
||||
public class OrganizationIntegrationConfigurationRepository(GlobalSettings globalSettings)
|
||||
: IOrganizationIntegrationConfigurationRepository
|
||||
{
|
||||
private readonly string _slackToken = globalSettings.EventLogging.SlackToken;
|
||||
private readonly string _slackUserEmail = globalSettings.EventLogging.SlackUserEmail;
|
||||
private readonly string _webhookUrl = globalSettings.EventLogging.WebhookUrl;
|
||||
|
||||
public async Task<IntegrationConfiguration<T>?> GetConfigurationAsync<T>(
|
||||
Guid organizationId,
|
||||
IntegrationType integrationType,
|
||||
EventType eventType)
|
||||
{
|
||||
switch (integrationType)
|
||||
{
|
||||
case IntegrationType.Slack:
|
||||
if (string.IsNullOrWhiteSpace(_slackToken) || string.IsNullOrWhiteSpace(_slackUserEmail))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new IntegrationConfiguration<SlackConfiguration>()
|
||||
{
|
||||
Configuration = new SlackConfiguration
|
||||
{
|
||||
Token = _slackToken,
|
||||
Channels = new List<string> { },
|
||||
UserEmails = new List<string> { _slackUserEmail }
|
||||
},
|
||||
Template = "This is a test of the new Slack integration"
|
||||
} as IntegrationConfiguration<T>;
|
||||
case IntegrationType.Webhook:
|
||||
if (string.IsNullOrWhiteSpace(_webhookUrl))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new IntegrationConfiguration<WebhookConfiguration>()
|
||||
{
|
||||
Configuration = new WebhookConfiguration()
|
||||
{
|
||||
Url = _webhookUrl,
|
||||
},
|
||||
Template = "{ \"newObject\": true }"
|
||||
} as IntegrationConfiguration<T>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<IntegrationConfiguration<T>>> GetAllConfigurationsAsync<T>(Guid organizationId) => throw new NotImplementedException();
|
||||
|
||||
public async Task AddConfigurationAsync<T>(Guid organizationId, IntegrationType integrationType, EventType eventType,
|
||||
IntegrationConfiguration<T> configuration) =>
|
||||
throw new NotImplementedException();
|
||||
|
||||
public async Task UpdateConfigurationAsync<T>(IntegrationConfiguration<T> configuration) => throw new NotImplementedException();
|
||||
|
||||
public async Task DeleteConfigurationAsync(Guid id) => throw new NotImplementedException();
|
||||
}
|
@ -1,32 +1,38 @@
|
||||
using System.Text.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Models.Data.Integrations;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class SlackEventHandler(
|
||||
GlobalSettings globalSettings,
|
||||
SlackMessageSender slackMessageSender)
|
||||
: IEventMessageHandler
|
||||
IOrganizationIntegrationConfigurationRepository configurationRepository,
|
||||
SlackMessageSender slackMessageSender
|
||||
) : IEventMessageHandler
|
||||
{
|
||||
private readonly string _token = globalSettings.EventLogging.SlackToken;
|
||||
private readonly string _email = globalSettings.EventLogging.SlackUserEmail;
|
||||
|
||||
public async Task HandleEventAsync(EventMessage eventMessage)
|
||||
{
|
||||
await slackMessageSender.SendDirectMessageByEmailAsync(
|
||||
_token,
|
||||
JsonSerializer.Serialize(eventMessage),
|
||||
_email
|
||||
);
|
||||
Guid organizationId = eventMessage.OrganizationId ?? Guid.NewGuid();
|
||||
|
||||
var configuration = await configurationRepository.GetConfigurationAsync<SlackConfiguration>(
|
||||
organizationId,
|
||||
IntegrationType.Slack,
|
||||
eventMessage.Type);
|
||||
if (configuration is not null)
|
||||
{
|
||||
await slackMessageSender.SendDirectMessageByEmailAsync(
|
||||
configuration.Configuration.Token,
|
||||
configuration.Template,
|
||||
configuration.Configuration.UserEmails.First()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleManyEventsAsync(IEnumerable<EventMessage> eventMessages)
|
||||
{
|
||||
await slackMessageSender.SendDirectMessageByEmailAsync(
|
||||
_token,
|
||||
JsonSerializer.Serialize(eventMessages),
|
||||
_email
|
||||
);
|
||||
foreach (var eventMessage in eventMessages)
|
||||
{
|
||||
await HandleEventAsync(eventMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +1,45 @@
|
||||
using System.Net.Http.Json;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Models.Data.Integrations;
|
||||
using Bit.Core.Repositories;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Bit.Core.Services;
|
||||
|
||||
public class WebhookEventHandler(
|
||||
IHttpClientFactory httpClientFactory,
|
||||
GlobalSettings globalSettings)
|
||||
IOrganizationIntegrationConfigurationRepository configurationRepository)
|
||||
: IEventMessageHandler
|
||||
{
|
||||
private readonly HttpClient _httpClient = httpClientFactory.CreateClient(HttpClientName);
|
||||
private readonly string _webhookUrl = globalSettings.EventLogging.WebhookUrl;
|
||||
|
||||
public const string HttpClientName = "WebhookEventHandlerHttpClient";
|
||||
|
||||
public async Task HandleEventAsync(EventMessage eventMessage)
|
||||
{
|
||||
var content = JsonContent.Create(eventMessage);
|
||||
var response = await _httpClient.PostAsync(_webhookUrl, content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
Guid organizationId = eventMessage.OrganizationId ?? Guid.NewGuid();
|
||||
|
||||
var configuration = await configurationRepository.GetConfigurationAsync<WebhookConfiguration>(
|
||||
organizationId,
|
||||
IntegrationType.Webhook,
|
||||
eventMessage.Type);
|
||||
if (configuration is not null)
|
||||
{
|
||||
var content = JsonContent.Create(configuration.Template);
|
||||
var response = await _httpClient.PostAsync(
|
||||
configuration.Configuration.Url,
|
||||
content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task HandleManyEventsAsync(IEnumerable<EventMessage> eventMessages)
|
||||
{
|
||||
var content = JsonContent.Create(eventMessages);
|
||||
var response = await _httpClient.PostAsync(_webhookUrl, content);
|
||||
response.EnsureSuccessStatusCode();
|
||||
foreach (var eventMessage in eventMessages)
|
||||
{
|
||||
await HandleEventAsync(eventMessage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -74,6 +74,7 @@
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="AdminConsole\Utilities\" />
|
||||
<Folder Include="Resources\" />
|
||||
<Folder Include="Properties\" />
|
||||
</ItemGroup>
|
||||
|
@ -2,6 +2,7 @@
|
||||
using Bit.Core.AdminConsole.Services.Implementations;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.IdentityServer;
|
||||
using Bit.Core.Repositories;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Settings;
|
||||
using Bit.Core.Utilities;
|
||||
@ -117,33 +118,26 @@ public class Startup
|
||||
globalSettings,
|
||||
globalSettings.EventLogging.RabbitMq.EventRepositoryQueueName));
|
||||
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.SlackToken) &&
|
||||
CoreHelpers.SettingHasValue(globalSettings.EventLogging.SlackUserEmail))
|
||||
{
|
||||
services.AddHttpClient(SlackMessageSender.HttpClientName);
|
||||
services.AddSingleton<SlackMessageSender>();
|
||||
services.AddSingleton<SlackEventHandler>();
|
||||
services.AddSingleton<IOrganizationIntegrationConfigurationRepository, OrganizationIntegrationConfigurationRepository>();
|
||||
|
||||
services.AddSingleton<IHostedService>(provider =>
|
||||
new RabbitMqEventListenerService(
|
||||
provider.GetRequiredService<SlackEventHandler>(),
|
||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
||||
globalSettings,
|
||||
globalSettings.EventLogging.RabbitMq.SlackQueueName));
|
||||
}
|
||||
services.AddHttpClient(SlackMessageSender.HttpClientName);
|
||||
services.AddSingleton<SlackMessageSender>();
|
||||
services.AddSingleton<SlackEventHandler>();
|
||||
|
||||
if (CoreHelpers.SettingHasValue(globalSettings.EventLogging.WebhookUrl))
|
||||
{
|
||||
services.AddSingleton<WebhookEventHandler>();
|
||||
services.AddHttpClient(WebhookEventHandler.HttpClientName);
|
||||
services.AddSingleton<IHostedService>(provider =>
|
||||
new RabbitMqEventListenerService(
|
||||
provider.GetRequiredService<SlackEventHandler>(),
|
||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
||||
globalSettings,
|
||||
globalSettings.EventLogging.RabbitMq.SlackQueueName));
|
||||
|
||||
services.AddSingleton<IHostedService>(provider =>
|
||||
new RabbitMqEventListenerService(
|
||||
provider.GetRequiredService<WebhookEventHandler>(),
|
||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
||||
globalSettings,
|
||||
globalSettings.EventLogging.RabbitMq.WebhookQueueName));
|
||||
}
|
||||
services.AddSingleton<WebhookEventHandler>();
|
||||
services.AddSingleton<IHostedService>(provider =>
|
||||
new RabbitMqEventListenerService(
|
||||
provider.GetRequiredService<WebhookEventHandler>(),
|
||||
provider.GetRequiredService<ILogger<RabbitMqEventListenerService>>(),
|
||||
globalSettings,
|
||||
globalSettings.EventLogging.RabbitMq.WebhookQueueName));
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user