Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ public static void AddAdminConsoleAuthorizationHandlers(this IServiceCollection
services.TryAddScoped<IOrganizationContext, OrganizationContext>();

services.TryAddEnumerable([
ServiceDescriptor.Scoped<IAuthorizationHandler, BulkCollectionAuthorizationHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, CollectionAuthorizationHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, GroupAuthorizationHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, OrganizationRequirementHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, RecoverAccountAuthorizationHandler>(),
]);
ServiceDescriptor.Scoped<IAuthorizationHandler, BulkCollectionAuthorizationHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, CollectionAuthorizationHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, GroupAuthorizationHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, OrganizationRequirementHandler>(),
ServiceDescriptor.Scoped<IAuthorizationHandler, RecoverAccountAuthorizationHandler>(),
]);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Delivery;

public class AmazonSesMailDeliveryService : IMailDeliveryService, IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
๏ปฟusing Bit.Core.Models.Mail;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Delivery;

public interface IMailDeliveryService
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
using Microsoft.Extensions.Logging;
using MimeKit;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Delivery;

public class MailKitSmtpMailDeliveryService : IMailDeliveryService
{
Expand Down Expand Up @@ -81,7 +81,7 @@
{
if (_globalSettings.Mail.Smtp.TrustServer)
{
client.ServerCertificateValidationCallback = (s, c, h, e) => true;

Check warning on line 84 in src/Core/Platform/Mail/Delivery/MailKitSmtpMailDeliveryService.cs

View workflow job for this annotation

GitHub Actions / Sonar / Quality scan

Enable server certificate validation on this SSL/TLS connection (https://rules.sonarsource.com/csharp/RSPEC-4830)
}

if (!_globalSettings.Mail.Smtp.StartTls && !_globalSettings.Mail.Smtp.Ssl &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Delivery;

public class MultiServiceMailDeliveryService : IMailDeliveryService
{
Expand All @@ -11,7 +11,7 @@
private readonly IMailDeliveryService _sendGridService;
private readonly int _sendGridPercentage;

private static Random _random = new Random();

Check warning on line 14 in src/Core/Platform/Mail/Delivery/MultiServiceMailDeliveryService.cs

View workflow job for this annotation

GitHub Actions / Sonar / Quality scan

Make sure that using this pseudorandom number generator is safe here. (https://rules.sonarsource.com/csharp/RSPEC-2245)

public MultiServiceMailDeliveryService(
GlobalSettings globalSettings,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
๏ปฟusing Bit.Core.Models.Mail;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Delivery;

public class NoopMailDeliveryService : IMailDeliveryService
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
using SendGrid;
using SendGrid.Helpers.Mail;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Delivery;

public class SendGridMailDeliveryService : IMailDeliveryService, IDisposable
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
๏ปฟusing Azure.Storage.Queues;
using Bit.Core.Models.Mail;
using Bit.Core.Services;
using Bit.Core.Settings;
using Bit.Core.Utilities;

namespace Bit.Core.Services;

namespace Bit.Core.Platform.Mail.Enqueuing;
public class AzureQueueMailService : AzureQueueService<IMailQueueMessage>, IMailEnqueuingService
{
public AzureQueueMailService(GlobalSettings globalSettings) : base(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
๏ปฟusing Bit.Core.Models.Mail;

namespace Bit.Core.Services;

namespace Bit.Core.Platform.Mail.Enqueuing;
public class BlockingMailEnqueuingService : IMailEnqueuingService
{
public async Task EnqueueAsync(IMailQueueMessage message, Func<IMailQueueMessage, Task> fallback)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
๏ปฟusing Bit.Core.Models.Mail;

namespace Bit.Core.Services;
namespace Bit.Core.Platform.Mail.Enqueuing;

public interface IMailEnqueuingService
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
using Bit.Core.Models.Mail.Billing;
using Bit.Core.Models.Mail.FamiliesForEnterprise;
using Bit.Core.Models.Mail.Provider;
using Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Platform.Mail.Enqueuing;
using Bit.Core.SecretsManager.Models.Mail;
using Bit.Core.Settings;
using Bit.Core.Utilities;
Expand All @@ -28,8 +30,9 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;

namespace Bit.Core.Services;
namespace Bit.Core.Services.Mail;

[Obsolete("The IMailService has been deprecated in favor of the IMailer. All new emails should be sent with an IMailer implementation.")]
public class HandlebarsMailService : IMailService
{
private const string Namespace = "Bit.Core.MailTemplates.Handlebars";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

namespace Bit.Core.Services;

[Obsolete("The IMailService has been deprecated in favor of the IMailer. All new emails should be sent with an IMailer implementation.")]
public interface IMailService
{
Task SendWelcomeEmailAsync(User user);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
๏ปฟnamespace Bit.Core.Platform.Mailer;
๏ปฟnamespace Bit.Core.Platform.Mail.Mailer;

#nullable enable

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
using System.Reflection;
using HandlebarsDotNet;

namespace Bit.Core.Platform.Mailer;

namespace Bit.Core.Platform.Mail.Mailer;
public class HandlebarMailRenderer : IMailRenderer
{
/// <summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
๏ปฟ#nullable enable
namespace Bit.Core.Platform.Mailer;
namespace Bit.Core.Platform.Mail.Mailer;

public interface IMailRenderer
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
๏ปฟnamespace Bit.Core.Platform.Mailer;
๏ปฟnamespace Bit.Core.Platform.Mail.Mailer;

#nullable enable

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
๏ปฟusing Bit.Core.Models.Mail;
using Bit.Core.Services;
using Bit.Core.Platform.Mail.Delivery;

namespace Bit.Core.Platform.Mailer;
namespace Bit.Core.Platform.Mail.Mailer;

#nullable enable

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
๏ปฟusing Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;

namespace Bit.Core.Platform.Mailer;
namespace Bit.Core.Platform.Mail.Mailer;

#nullable enable

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

namespace Bit.Core.Services;

[Obsolete("The IMailService has been deprecated in favor of the IMailer. All new emails should be sent with an IMailer implementation.")]
public class NoopMailService : IMailService
{
public Task SendChangeEmailAlreadyExistsEmailAsync(string fromEmail, string toEmail)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
# Mailer
# Mail Services
## `MailService`

The `MailService` and its implementation in `HandlebarsMailService` has been deprecated in favor of the `Mailer` implementation.

New emails should be implemented using [MJML](../../MailTemplates/README.md) and the `Mailer`.

## `Mailer`

The Mailer feature provides a structured, type-safe approach to sending emails in the Bitwarden server application. It
uses Handlebars templates to render both HTML and plain text email content.

## Architecture
### Architecture

The Mailer system consists of four main components:

Expand All @@ -12,17 +19,17 @@ The Mailer system consists of four main components:
3. **BaseMailView** - Abstract base class for email template view models
4. **IMailRenderer** - Internal interface for rendering templates (implemented by `HandlebarMailRenderer`)

## How To Use
### How To Use

1. Define a view model that inherits from `BaseMailView` with properties for template data
2. Create Handlebars templates (`.html.hbs` and `.text.hbs`) as embedded resources, preferably using the MJML pipeline,
`/src/Core/MailTemplates/Mjml`.
3. Define an email class that inherits from `BaseMail<TView>` with metadata like subject
4. Use `IMailer.SendEmail()` to render and send the email

## Creating a New Email
### Creating a New Email

### Step 1: Define the Email & View Model
#### Step 1: Define the Email & View Model

Create a class that inherits from `BaseMailView`:

Expand All @@ -43,7 +50,7 @@ public class WelcomeEmail : BaseMail<WelcomeEmailView>
}
```

### Step 2: Create Handlebars Templates
#### Step 2: Create Handlebars Templates

Create two template files as embedded resources next to your view model. **Important**: The file names must be located
directly next to the `ViewClass` and match the name of the view.
Expand Down Expand Up @@ -80,7 +87,7 @@ Activate your account: {{ ActivationUrl }}
</ItemGroup>
```

### Step 3: Send the Email
#### Step 3: Send the Email

Inject `IMailer` and send the email, this may be done in a service, command or some other application layer.

Expand Down Expand Up @@ -111,9 +118,9 @@ public class SomeService
}
```

## Advanced Features
### Advanced Features

### Multiple Recipients
#### Multiple Recipients

Send to multiple recipients by providing multiple email addresses:

Expand All @@ -125,7 +132,7 @@ var mail = new WelcomeEmail
};
```

### Bypass Suppression List
#### Bypass Suppression List

For critical emails like account recovery or email OTP, you can bypass the suppression list:

Expand All @@ -139,7 +146,7 @@ public class PasswordResetEmail : BaseMail<PasswordResetEmailView>

**Warning**: Only use `IgnoreSuppressList = true` for critical account recovery or authentication emails.

### Email Categories
#### Email Categories

Optionally categorize emails for processing at the upstream email delivery service:

Expand All @@ -151,7 +158,7 @@ public class MarketingEmail : BaseMail<MarketingEmailView>
}
```

## Built-in View Properties
### Built-in View Properties

All view models inherit from `BaseMailView`, which provides:

Expand All @@ -162,7 +169,7 @@ All view models inherit from `BaseMailView`, which provides:
<footer>&copy; {{ CurrentYear }} Bitwarden Inc.</footer>
```

## Template Naming Convention
### Template Naming Convention

Templates must follow this naming convention:

Expand Down Expand Up @@ -193,8 +200,14 @@ services.TryAddSingleton<IMailRenderer, HandlebarMailRenderer>();
services.TryAddSingleton<IMailer, Mailer>();
```

## Performance Notes
### Performance Notes

- **Template caching** - `HandlebarMailRenderer` automatically caches compiled templates
- **Lazy initialization** - Handlebars is initialized only when first needed
- **Thread-safe** - The renderer is thread-safe for concurrent email rendering

# Overriding email templates from disk

The mail services support loading the mail template from disk. This is intended to be used by self-hosted customers who want to modify their email appearance. These overrides are not intended to be used during local development, as any changes there would not be reflected in the templates used in a normal deployment configuration.

Any customer using this override has worked with Bitwarden support on an approved implementation and has acknowledged that they are responsible for reacting to any changes made to the templates as a part of the Bitwarden development process. This includes, but is not limited to, changes in Handlebars property names, removal of properties from the `ViewModel` classes, and changes in template names. **Bitwarden is not responsible for maintaining backward compatibility between releases in order to support any overridden emails.**
5 changes: 4 additions & 1 deletion src/SharedWeb/Utilities/ServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
using Bit.Core.NotificationCenter;
using Bit.Core.OrganizationFeatures;
using Bit.Core.Platform;
using Bit.Core.Platform.Mailer;
using Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Platform.Mail.Enqueuing;
using Bit.Core.Platform.Mail.Mailer;
using Bit.Core.Platform.Push;
using Bit.Core.Platform.PushRegistration.Internal;
using Bit.Core.Repositories;
Expand All @@ -47,6 +49,7 @@
using Bit.Core.SecretsManager.Repositories.Noop;
using Bit.Core.Services;
using Bit.Core.Services.Implementations;
using Bit.Core.Services.Mail;
using Bit.Core.Settings;
using Bit.Core.Tokens;
using Bit.Core.Tools.ImportFeatures;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
๏ปฟusing System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Bit.Core.Models.Mail;
using Bit.Core.Services;
using Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Settings;
using MailKit.Security;
using Microsoft.Extensions.Logging;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
๏ปฟusing Bit.Core.Platform.Mailer;
๏ปฟusing Bit.Core.Platform.Mail.Mailer;
using Bit.Core.Test.Platform.Mailer.TestMail;
using Xunit;

Expand Down
7 changes: 3 additions & 4 deletions test/Core.Test/Platform/Mailer/MailerTest.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,18 @@
๏ปฟusing Bit.Core.Models.Mail;
using Bit.Core.Platform.Mailer;
using Bit.Core.Services;
using Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Platform.Mail.Mailer;
using Bit.Core.Test.Platform.Mailer.TestMail;
using NSubstitute;
using Xunit;

namespace Bit.Core.Test.Platform.Mailer;

public class MailerTest
{
[Fact]
public async Task SendEmailAsync()
{
var deliveryService = Substitute.For<IMailDeliveryService>();
var mailer = new Core.Platform.Mailer.Mailer(new HandlebarMailRenderer(), deliveryService);
var mailer = new Core.Platform.Mail.Mailer.Mailer(new HandlebarMailRenderer(), deliveryService);

var mail = new TestMail.TestMail()
{
Expand Down
2 changes: 1 addition & 1 deletion test/Core.Test/Platform/Mailer/TestMail/TestMailView.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
๏ปฟusing Bit.Core.Platform.Mailer;
๏ปฟusing Bit.Core.Platform.Mail.Mailer;

namespace Bit.Core.Test.Platform.Mailer.TestMail;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
๏ปฟusing Amazon.SimpleEmail;
using Amazon.SimpleEmail.Model;
using Bit.Core.Models.Mail;
using Bit.Core.Services;
using Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Settings;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
Expand Down
3 changes: 3 additions & 0 deletions test/Core.Test/Services/HandlebarsMailServiceTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@
using Bit.Core.Auth.Models.Business;
using Bit.Core.Entities;
using Bit.Core.Models.Mail;
using Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Platform.Mail.Enqueuing;
using Bit.Core.Services;
using Bit.Core.Services.Mail;
using Bit.Core.Settings;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Logging;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
๏ปฟusing Bit.Core.Services;
๏ปฟusing Bit.Core.Platform.Mail.Delivery;
using Bit.Core.Settings;
using Microsoft.Extensions.Logging;
using NSubstitute;
Expand Down
Loading
Loading