From 2b97c17f68e6c0bbdede743460de6e6522f000cb Mon Sep 17 00:00:00 2001
From: fraliv13 <5892139+fraliv13@users.noreply.github.com>
Date: Mon, 17 Jun 2024 10:48:43 +0300
Subject: [PATCH 1/6] Multi-tenant messaging: filter out events from unknown
tenants
---
.../NBB.Messaging.MultiTenancy.csproj | 1 +
.../TenantMiddleware.cs | 59 +++++++++++++++++--
.../CachedTenantRepositoryDecorator.cs | 20 +++++++
.../ConfigurationTenantRepository.cs | 10 ++++
.../Repositories/ITenantRepository.cs | 1 +
5 files changed, 85 insertions(+), 6 deletions(-)
diff --git a/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj b/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj
index 1db817d0..5dbd641f 100644
--- a/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj
+++ b/src/Messaging/NBB.Messaging.MultiTenancy/NBB.Messaging.MultiTenancy.csproj
@@ -15,6 +15,7 @@
+
diff --git a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
index 5f4614ab..9943f16c 100644
--- a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
+++ b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
@@ -13,6 +13,9 @@
using NBB.MultiTenancy.Identification.Services;
using NBB.MultiTenancy.Abstractions;
using System.Diagnostics;
+using Microsoft.Extensions.Logging;
+using MediatR;
+using System.Linq;
namespace NBB.Messaging.MultiTenancy
{
@@ -27,13 +30,15 @@ public class TenantMiddleware : IPipelineMiddleware
private readonly ITenantIdentificationService _tenantIdentificationService;
private readonly IOptions _tenancyOptions;
private readonly ITenantRepository _tenantRepository;
+ private readonly ILogger _logger;
- public TenantMiddleware(ITenantContextAccessor tenantContextAccessor, ITenantIdentificationService tenantIdentificationService, IOptions tenancyOptions, ITenantRepository tenantRepository)
+ public TenantMiddleware(ITenantContextAccessor tenantContextAccessor, ITenantIdentificationService tenantIdentificationService, IOptions tenancyOptions, ITenantRepository tenantRepository, ILogger logger)
{
_tenantContextAccessor = tenantContextAccessor;
_tenantIdentificationService = tenantIdentificationService;
_tenancyOptions = tenancyOptions;
_tenantRepository = tenantRepository;
+ _logger = logger;
}
public async Task Invoke(MessagingContext context, CancellationToken cancellationToken, Func next)
@@ -45,21 +50,63 @@ public async Task Invoke(MessagingContext context, CancellationToken cancellatio
if (_tenancyOptions.Value.TenancyType == TenancyType.MonoTenant)
{
- _tenantContextAccessor.TenantContext = new TenantContext(Tenant.Default);
+ _tenantContextAccessor.TenantContext = new TenantContext(Tenant.Default);
await next();
return;
}
+ Tenant tenant;
+
+ if (context.MessagingEnvelope.Payload is INotification)
+ {
+ tenant = await TryLoadTenant(context.TopicName, cancellationToken);
+ if (tenant == null)
+ {
+ return;
+ }
+ }
+ else
+ {
+ tenant = await LoadTenant(cancellationToken);
+ }
+
+
+ _tenantContextAccessor.TenantContext = new TenantContext(tenant);
+
+ Activity.Current?.SetTag(TracingTags.TenantId, tenant.TenantId);
+
+ await next();
+ }
+
+ private async Task LoadTenant(CancellationToken cancellationToken)
+ {
var tenantId = await _tenantIdentificationService.GetTenantIdAsync();
var tenant = await _tenantRepository.Get(tenantId, cancellationToken)
- ?? throw new ApplicationException($"Tenant {tenantId} not found");
+ ?? throw new ApplicationException($"Tenant {tenantId} not found");
- _tenantContextAccessor.TenantContext = new TenantContext(tenant);
+ return tenant;
+ }
- Activity.Current?.SetTag(TracingTags.TenantId, tenantId);
- await next();
+ private async Task TryLoadTenant(string topic, CancellationToken cancellationToken)
+ {
+ var tenantId = await _tenantIdentificationService.TryGetTenantIdAsync();
+ if (!tenantId.HasValue)
+ {
+ _logger.LogDebug("Tenant could not be identified. Message {Topic} will be ignored.", topic);
+ return null;
+ }
+
+ var tenant = await _tenantRepository.TryGet(tenantId.Value, cancellationToken);
+ if (tenant == null)
+ {
+ _logger.LogDebug("Tenant {Tenant} not found or not enabled. Message {Topic} will be ignored.", tenantId.Value, topic);
+ return null;
+ }
+
+ return tenant;
}
+
}
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs
index d9716f41..c2dc92a3 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs
@@ -43,6 +43,26 @@ public async Task Get(Guid id, CancellationToken token)
return dbTenant;
}
+ public async Task TryGet(Guid id, CancellationToken token)
+ {
+ var cacheKey = CacheTenantByIdKey(id);
+ var cachedTenant = await GetTenantFromCache(cacheKey, token);
+ if (cachedTenant != null)
+ {
+ return cachedTenant;
+ }
+
+ var dbTenant = await _tenantRepository.TryGet(id, token);
+ if (dbTenant == null)
+ {
+ return null;
+ }
+
+ await SetTenantToCache(dbTenant, cacheKey, token);
+
+ return dbTenant;
+ }
+
private async Task GetTenantFromCache(string key, CancellationToken token = default)
{
var sTenant = await _cache.GetStringAsync(key, token);
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs
index 73c69efe..1ccfaf95 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs
@@ -54,6 +54,16 @@ public Task Get(Guid id, CancellationToken token = default)
return Task.FromResult(result.Enabled ? result : throw new Exception($"Tenant {result.Code} is disabled "));
}
+ public Task TryGet(Guid id, CancellationToken token = default)
+ {
+ if (!tenantMap.TryGetValue(id, out var result) || !result.Enabled)
+ {
+ return Task.FromResult(default(Tenant));
+ }
+
+ return Task.FromResult(result);
+ }
+
public Task GetByHost(string host, CancellationToken token = default)
{
throw new NotImplementedException();
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
index 9293e0e1..b405c7a1 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
@@ -11,6 +11,7 @@ namespace NBB.MultiTenancy.Abstractions.Repositories
public interface ITenantRepository
{
Task Get(Guid id, CancellationToken token = default);
+ Task TryGet(Guid id, CancellationToken token = default);
Task GetByHost(string host, CancellationToken token = default);
Task> GetAll(CancellationToken token = default);
}
From 696185c716ea13b058111859b7f8e7105f99da6b Mon Sep 17 00:00:00 2001
From: fraliv13 <5892139+fraliv13@users.noreply.github.com>
Date: Mon, 17 Jun 2024 11:04:11 +0300
Subject: [PATCH 2/6] Removed get from interface
---
.../CachedTenantRepositoryDecorator.cs | 20 -------------------
.../ConfigurationTenantRepository.cs | 11 ----------
.../Repositories/ITenantRepository.cs | 13 +++++++++++-
3 files changed, 12 insertions(+), 32 deletions(-)
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs
index c2dc92a3..8c5aadec 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/CachedTenantRepositoryDecorator.cs
@@ -23,26 +23,6 @@ public CachedTenantRepositoryDecorator(ITenantRepository tenantRepository, IDist
_cache = cache;
}
- public async Task Get(Guid id, CancellationToken token)
- {
- var cacheKey = CacheTenantByIdKey(id);
- var cachedTenant = await GetTenantFromCache(cacheKey, token);
- if (cachedTenant != null)
- {
- return cachedTenant;
- }
-
- var dbTenant = await _tenantRepository.Get(id, token);
- if (dbTenant == null)
- {
- return null;
- }
-
- await SetTenantToCache(dbTenant, cacheKey, token);
-
- return dbTenant;
- }
-
public async Task TryGet(Guid id, CancellationToken token)
{
var cacheKey = CacheTenantByIdKey(id);
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs
index 1ccfaf95..b1830014 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ConfigurationTenantRepository.cs
@@ -43,17 +43,6 @@ public ConfigurationTenantRepository(IConfiguration configuration, IOptions Get(Guid id, CancellationToken token = default)
- {
- if (!tenantMap.TryGetValue(id, out var result))
- {
- throw new TenantNotFoundException(id);
- }
-
-
- return Task.FromResult(result.Enabled ? result : throw new Exception($"Tenant {result.Code} is disabled "));
- }
-
public Task TryGet(Guid id, CancellationToken token = default)
{
if (!tenantMap.TryGetValue(id, out var result) || !result.Enabled)
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
index b405c7a1..ae53257d 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
@@ -10,9 +10,20 @@ namespace NBB.MultiTenancy.Abstractions.Repositories
{
public interface ITenantRepository
{
- Task Get(Guid id, CancellationToken token = default);
Task TryGet(Guid id, CancellationToken token = default);
Task GetByHost(string host, CancellationToken token = default);
Task> GetAll(CancellationToken token = default);
+
+ async Task Get(Guid id, CancellationToken token = default)
+ {
+ var result = await TryGet(id, token);
+ if (result == null)
+ {
+ throw new TenantNotFoundException(id);
+ }
+
+
+ return result.Enabled ? result : throw new Exception($"Tenant {result.Code} is disabled ");
+ }
}
}
From 86927d8cef2498b9b7a1861a99c17eca1aafe347 Mon Sep 17 00:00:00 2001
From: fraliv13 <5892139+fraliv13@users.noreply.github.com>
Date: Mon, 17 Jun 2024 11:05:45 +0300
Subject: [PATCH 3/6] Whitespace
---
.../Repositories/ITenantRepository.cs | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
index ae53257d..015248a6 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
@@ -22,7 +22,6 @@ async Task Get(Guid id, CancellationToken token = default)
throw new TenantNotFoundException(id);
}
-
return result.Enabled ? result : throw new Exception($"Tenant {result.Code} is disabled ");
}
}
From 6e5f140a944db65104d6d350103278ae8b9e7dec Mon Sep 17 00:00:00 2001
From: fraliv13 <5892139+fraliv13@users.noreply.github.com>
Date: Mon, 17 Jun 2024 11:25:52 +0300
Subject: [PATCH 4/6] Primary constructor
---
.../TenantMiddleware.cs | 43 ++++++++-----------
1 file changed, 17 insertions(+), 26 deletions(-)
diff --git a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
index 9943f16c..bb89f519 100644
--- a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
+++ b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
@@ -24,33 +24,24 @@ namespace NBB.Messaging.MultiTenancy
/// obtained from the current identification strategy and builds the tenant context.
///
///
- public class TenantMiddleware : IPipelineMiddleware
+ public class TenantMiddleware(
+ ITenantContextAccessor tenantContextAccessor,
+ ITenantIdentificationService tenantIdentificationService,
+ IOptions tenancyOptions,
+ ITenantRepository tenantRepository,
+ ILogger logger
+ ) : IPipelineMiddleware
{
- private readonly ITenantContextAccessor _tenantContextAccessor;
- private readonly ITenantIdentificationService _tenantIdentificationService;
- private readonly IOptions _tenancyOptions;
- private readonly ITenantRepository _tenantRepository;
- private readonly ILogger _logger;
-
- public TenantMiddleware(ITenantContextAccessor tenantContextAccessor, ITenantIdentificationService tenantIdentificationService, IOptions tenancyOptions, ITenantRepository tenantRepository, ILogger logger)
- {
- _tenantContextAccessor = tenantContextAccessor;
- _tenantIdentificationService = tenantIdentificationService;
- _tenancyOptions = tenancyOptions;
- _tenantRepository = tenantRepository;
- _logger = logger;
- }
-
public async Task Invoke(MessagingContext context, CancellationToken cancellationToken, Func next)
{
- if (_tenantContextAccessor.TenantContext != null)
+ if (tenantContextAccessor.TenantContext != null)
{
throw new ApplicationException("Tenant context is already set");
}
- if (_tenancyOptions.Value.TenancyType == TenancyType.MonoTenant)
+ if (tenancyOptions.Value.TenancyType == TenancyType.MonoTenant)
{
- _tenantContextAccessor.TenantContext = new TenantContext(Tenant.Default);
+ tenantContextAccessor.TenantContext = new TenantContext(Tenant.Default);
await next();
return;
}
@@ -71,7 +62,7 @@ public async Task Invoke(MessagingContext context, CancellationToken cancellatio
}
- _tenantContextAccessor.TenantContext = new TenantContext(tenant);
+ tenantContextAccessor.TenantContext = new TenantContext(tenant);
Activity.Current?.SetTag(TracingTags.TenantId, tenant.TenantId);
@@ -80,8 +71,8 @@ public async Task Invoke(MessagingContext context, CancellationToken cancellatio
private async Task LoadTenant(CancellationToken cancellationToken)
{
- var tenantId = await _tenantIdentificationService.GetTenantIdAsync();
- var tenant = await _tenantRepository.Get(tenantId, cancellationToken)
+ var tenantId = await tenantIdentificationService.GetTenantIdAsync();
+ var tenant = await tenantRepository.Get(tenantId, cancellationToken)
?? throw new ApplicationException($"Tenant {tenantId} not found");
return tenant;
@@ -90,17 +81,17 @@ private async Task LoadTenant(CancellationToken cancellationToken)
private async Task TryLoadTenant(string topic, CancellationToken cancellationToken)
{
- var tenantId = await _tenantIdentificationService.TryGetTenantIdAsync();
+ var tenantId = await tenantIdentificationService.TryGetTenantIdAsync();
if (!tenantId.HasValue)
{
- _logger.LogDebug("Tenant could not be identified. Message {Topic} will be ignored.", topic);
+ logger.LogDebug("Tenant could not be identified. Message {Topic} will be ignored.", topic);
return null;
}
- var tenant = await _tenantRepository.TryGet(tenantId.Value, cancellationToken);
+ var tenant = await tenantRepository.TryGet(tenantId.Value, cancellationToken);
if (tenant == null)
{
- _logger.LogDebug("Tenant {Tenant} not found or not enabled. Message {Topic} will be ignored.", tenantId.Value, topic);
+ logger.LogDebug("Tenant {Tenant} not found or not enabled. Message {Topic} will be ignored.", tenantId.Value, topic);
return null;
}
From fb2eb9881460b375755535e3691a6ea16ac4cb53 Mon Sep 17 00:00:00 2001
From: fraliv13 <5892139+fraliv13@users.noreply.github.com>
Date: Mon, 17 Jun 2024 11:45:16 +0300
Subject: [PATCH 5/6] fixed test
---
.../ConfigurationTenantRepositoryTests.cs | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs b/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs
index a4602ca5..ba7be638 100644
--- a/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs
+++ b/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs
@@ -190,7 +190,7 @@ public async Task get_tenant_should_throw_for_disabled_tenant()
var options = new OptionsWrapper(tenancyHostingOptions);
- var repo = new ConfigurationTenantRepository(configuration, options);
+ ITenantRepository repo = new ConfigurationTenantRepository(configuration, options);
//Act
Func action = async() =>
@@ -232,7 +232,7 @@ public async Task get_should_bind_tenant_code_from_section_name()
var repo = new ConfigurationTenantRepository(configuration, options);
//arrange
- var actual = await repo.Get(System.Guid.Parse("ef8d5362-9969-4e02-8794-0d1af56816f6"));
+ var actual = await repo.TryGet(Guid.Parse("ef8d5362-9969-4e02-8794-0d1af56816f6"));
// Assert
actual.Should().NotBeNull();
From f3b7802edd6531b1772f82f9c8660f825b5b7fb5 Mon Sep 17 00:00:00 2001
From: fraliv13 <5892139+fraliv13@users.noreply.github.com>
Date: Mon, 17 Jun 2024 12:01:05 +0300
Subject: [PATCH 6/6] Fix tests
---
.../NBB.Messaging.MultiTenancy/TenantMiddleware.cs | 2 +-
.../Repositories/ITenantRepository.cs | 10 +---------
.../ConfigurationTenantRepositoryTests.cs | 2 +-
3 files changed, 3 insertions(+), 11 deletions(-)
diff --git a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
index bb89f519..9dfd32f0 100644
--- a/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
+++ b/src/Messaging/NBB.Messaging.MultiTenancy/TenantMiddleware.cs
@@ -31,7 +31,7 @@ public class TenantMiddleware(
ITenantRepository tenantRepository,
ILogger logger
) : IPipelineMiddleware
- {
+ {
public async Task Invoke(MessagingContext context, CancellationToken cancellationToken, Func next)
{
if (tenantContextAccessor.TenantContext != null)
diff --git a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
index 015248a6..0ebf09d1 100644
--- a/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
+++ b/src/MultiTenancy/NBB.MultiTenancy.Abstractions/Repositories/ITenantRepository.cs
@@ -15,14 +15,6 @@ public interface ITenantRepository
Task> GetAll(CancellationToken token = default);
async Task Get(Guid id, CancellationToken token = default)
- {
- var result = await TryGet(id, token);
- if (result == null)
- {
- throw new TenantNotFoundException(id);
- }
-
- return result.Enabled ? result : throw new Exception($"Tenant {result.Code} is disabled ");
- }
+ => await TryGet(id, token) ?? throw new TenantNotFoundException(id);
}
}
diff --git a/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs b/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs
index ba7be638..b9b8456c 100644
--- a/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs
+++ b/test/UnitTests/MultiTenancy/NBB.MultiTenancy.Configuration.Tests/ConfigurationTenantRepositoryTests.cs
@@ -197,7 +197,7 @@ public async Task get_tenant_should_throw_for_disabled_tenant()
await repo.Get(Guid.Parse(tenantId));
//Assert
- await action.Should().ThrowAsync().WithMessage("*disabled*");
+ await action.Should().ThrowAsync();
}
[Fact]