From bc0165d892bf4c488a5e626b084aca569eac2ec6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Sun, 8 Jan 2017 22:35:47 +0300 Subject: [PATCH] Added tenant resolvers. --- .../AbpAspNetCoreMultiTenancyConsts.cs | 8 ++++++ .../AbpAspNetMultiTenancyModule.cs | 11 +++----- .../MultiTenancy/CookieTenantResolver.cs | 12 +++++++++ .../MultiTenancy/HeaderTenantResolver.cs | 13 ++++++++++ .../MultiTenancy/HttpTenantResolverBase.cs | 26 +++++++++++++++++++ .../MultiTenancy/QueryStringTenantResolver.cs | 23 ++++------------ .../MultiTenancy/RouteTenantResolver.cs | 20 ++++++++++++++ .../Abp/MultiTenancy/MultiTenancyManager.cs | 3 +++ .../AspNetCoreMultiTenancy_Tests.cs | 25 +++++++++++++++++- 9 files changed, 115 insertions(+), 26 deletions(-) create mode 100644 src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyConsts.cs create mode 100644 src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs create mode 100644 src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs create mode 100644 src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs create mode 100644 src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyConsts.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyConsts.cs new file mode 100644 index 0000000000..2bc830b59c --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetCoreMultiTenancyConsts.cs @@ -0,0 +1,8 @@ +namespace Volo.Abp.AspNetCore.MultiTenancy +{ + public static class AbpAspNetCoreMultiTenancyConsts + { + //TODO: Get from an option instead of a constant! + public const string TenantIdKey = "__tenantId"; + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetMultiTenancyModule.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetMultiTenancyModule.cs index d7f695d120..6a1b00a8d8 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetMultiTenancyModule.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/AbpAspNetMultiTenancyModule.cs @@ -11,14 +11,11 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { services.Configure(options => { + //TODO: domain/subdomain (not added by default) as first resolver! options.TenantResolvers.Insert(0, new QueryStringTenantResolver()); - //TODO: Web tenant resolvers: - // values can be tenant id or name, should be validated first by an abstraction (say, ITenantStore over ITenantManager)! - //- domain/subdomain (not added by default) - //- route {tenantId} - //+ querystring - //- header - //- cookie + options.TenantResolvers.Insert(1, new RouteTenantResolver()); + options.TenantResolvers.Insert(2, new HeaderTenantResolver()); + options.TenantResolvers.Insert(3, new CookieTenantResolver()); }); services.AddAssemblyOf(); diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs new file mode 100644 index 0000000000..b73c9b9678 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/CookieTenantResolver.cs @@ -0,0 +1,12 @@ +using Microsoft.AspNetCore.Http; + +namespace Volo.Abp.AspNetCore.MultiTenancy +{ + public class CookieTenantResolver : HttpTenantResolverBase + { + protected override string GetTenantIdFromHttpContextOrNull(HttpContext httpContext) + { + return httpContext.Request.Cookies[AbpAspNetCoreMultiTenancyConsts.TenantIdKey]; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs new file mode 100644 index 0000000000..a15897d566 --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HeaderTenantResolver.cs @@ -0,0 +1,13 @@ +using Microsoft.AspNetCore.Http; + +namespace Volo.Abp.AspNetCore.MultiTenancy +{ + public class HeaderTenantResolver : HttpTenantResolverBase + { + protected override string GetTenantIdFromHttpContextOrNull(HttpContext httpContext) + { + //TODO: Get first one if provided multiple values and write a log + return httpContext.Request.Headers[AbpAspNetCoreMultiTenancyConsts.TenantIdKey]; + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs new file mode 100644 index 0000000000..773dde007c --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/HttpTenantResolverBase.cs @@ -0,0 +1,26 @@ +using Microsoft.AspNetCore.Http; +using Volo.Abp.MultiTenancy; +using Volo.ExtensionMethods; + +namespace Volo.Abp.AspNetCore.MultiTenancy +{ + public abstract class HttpTenantResolverBase : ITenantResolver + { + public virtual void Resolve(ITenantResolveContext context) + { + var httpContext = context.GetHttpContext(); + if (httpContext == null) + { + return; + } + + var tenantId = GetTenantIdFromHttpContextOrNull(httpContext); + if (!tenantId.IsNullOrEmpty()) + { + context.Tenant = new TenantInfo(tenantId); + } + } + + protected abstract string GetTenantIdFromHttpContextOrNull(HttpContext httpContext); + } +} \ No newline at end of file diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs index 79f3a1e626..ab5c4ad59f 100644 --- a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/QueryStringTenantResolver.cs @@ -1,30 +1,17 @@ -using Volo.Abp.MultiTenancy; -using Volo.ExtensionMethods.Collections.Generic; +using Microsoft.AspNetCore.Http; namespace Volo.Abp.AspNetCore.MultiTenancy { - public class QueryStringTenantResolver : ITenantResolver + public class QueryStringTenantResolver : HttpTenantResolverBase { - public const string TenantIdKey = "__tenantId"; - - public void Resolve(ITenantResolveContext context) + protected override string GetTenantIdFromHttpContextOrNull(HttpContext httpContext) { - var httpContext = context.GetHttpContext(); - if (httpContext == null) - { - return; - } - if (!httpContext.Request.QueryString.HasValue) { - return; + return null; } - var tenantId = httpContext.Request.Query[TenantIdKey]; - if (!tenantId.IsNullOrEmpty()) - { - context.Tenant = new TenantInfo(tenantId); - } + return httpContext.Request.Query[AbpAspNetCoreMultiTenancyConsts.TenantIdKey]; } } } diff --git a/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs new file mode 100644 index 0000000000..efff74461c --- /dev/null +++ b/src/Volo.Abp.AspNetCore.MultiTenancy/Volo/Abp/AspNetCore/MultiTenancy/RouteTenantResolver.cs @@ -0,0 +1,20 @@ +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Routing; + +namespace Volo.Abp.AspNetCore.MultiTenancy +{ + public class RouteTenantResolver : HttpTenantResolverBase + { + protected override string GetTenantIdFromHttpContextOrNull(HttpContext httpContext) + { + var tenantId = httpContext.GetRouteValue(AbpAspNetCoreMultiTenancyConsts.TenantIdKey); + if (tenantId == null) + { + return null; + } + + return Convert.ToString(tenantId); + } + } +} \ No newline at end of file diff --git a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs index b0a6bff319..1d89e28b6c 100644 --- a/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs +++ b/src/Volo.Abp.MultiTenancy/Volo/Abp/MultiTenancy/MultiTenancyManager.cs @@ -48,6 +48,9 @@ namespace Volo.Abp.MultiTenancy foreach (var tenantResolver in _options.TenantResolvers) { tenantResolver.Resolve(context); + + //TODO: Validate TenantId by a TenantStore (TenantId can be TenancyName, so also normalize it by tenant store) + if (context.IsHandled()) { break; diff --git a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs index 69962ecf01..cfca0ce91a 100644 --- a/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs +++ b/test/Volo.Abp.AspNetCore.MultiTenancy.Tests/Volo/Abp/AspNetCore/MultiTenancy/AspNetCoreMultiTenancy_Tests.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Threading.Tasks; +using Microsoft.Net.Http.Headers; using Shouldly; using Volo.Abp.AspNetCore.App; using Xunit; @@ -20,7 +21,29 @@ namespace Volo.Abp.AspNetCore.MultiTenancy { const string testTenantId = "42"; - var result = await GetResponseAsObjectAsync>($"http://abp.io?{QueryStringTenantResolver.TenantIdKey}={testTenantId}"); + var result = await GetResponseAsObjectAsync>($"http://abp.io?{AbpAspNetCoreMultiTenancyConsts.TenantIdKey}={testTenantId}"); + result["TenantId"].ShouldBe(testTenantId); + } + + [Fact] + public async Task Should_Use_Header_Tenant_Id_If_Specified() + { + const string testTenantId = "42"; + + Client.DefaultRequestHeaders.Add(AbpAspNetCoreMultiTenancyConsts.TenantIdKey, testTenantId); + + var result = await GetResponseAsObjectAsync>("http://abp.io"); + result["TenantId"].ShouldBe(testTenantId); + } + + [Fact] + public async Task Should_Use_Cookie_Tenant_Id_If_Specified() + { + const string testTenantId = "42"; + + Client.DefaultRequestHeaders.Add("Cookie", new CookieHeaderValue(AbpAspNetCoreMultiTenancyConsts.TenantIdKey, testTenantId).ToString()); + + var result = await GetResponseAsObjectAsync>("http://abp.io"); result["TenantId"].ShouldBe(testTenantId); } }