Handle multitenancy in microservices.

pull/221/head
Halil İbrahim Kalkan 8 years ago
parent 946bd7fb79
commit f91fccc510

@ -26,7 +26,7 @@ namespace MicroserviceDemo.AuthServer
.AddDebug()
.AddSerilog(new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.RollingFile("Logs/logs.txt")
.WriteTo.File("Logs/logs.txt")
.CreateLogger()
);

@ -20,6 +20,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Volo.Abp.AspNetCore.MultiTenancy\Volo.Abp.AspNetCore.MultiTenancy.csproj" />
<ProjectReference Include="..\..\Volo.Abp.Autofac\Volo.Abp.Autofac.csproj" />
<ProjectReference Include="..\..\Volo.Abp.MultiTenancy.Application\Volo.Abp.MultiTenancy.Application.csproj" />
<ProjectReference Include="..\..\Volo.Abp.MultiTenancy.EntityFrameworkCore\Volo.Abp.MultiTenancy.EntityFrameworkCore.csproj" />

@ -6,7 +6,7 @@ using Microsoft.Extensions.DependencyInjection;
using Swashbuckle.AspNetCore.Swagger;
using Volo.Abp;
using Volo.Abp.AspNetCore.Modularity;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.AspNetCore.MultiTenancy;
using Volo.Abp.Autofac;
using Volo.Abp.Data;
using Volo.Abp.EntityFrameworkCore;
@ -15,7 +15,6 @@ using Volo.Abp.MultiTenancy;
using Volo.Abp.MultiTenancy.EntityFrameworkCore;
using Volo.Abp.Permissions.EntityFrameworkCore;
using Volo.Abp.Security.Claims;
using Volo.Abp.Threading;
namespace MicroserviceDemo.TenancyService
{
@ -24,6 +23,7 @@ namespace MicroserviceDemo.TenancyService
[DependsOn(typeof(AbpMultiTenancyHttpApiModule))]
[DependsOn(typeof(AbpMultiTenancyApplicationModule))]
[DependsOn(typeof(AbpPermissionsEntityFrameworkCoreModule))]
[DependsOn(typeof(AbpAspNetCoreMultiTenancyModule))]
public class MicroservicesDemoTenancyServiceModule : AbpModule
{
public override void ConfigureServices(IServiceCollection services)
@ -83,9 +83,6 @@ namespace MicroserviceDemo.TenancyService
{
var app = context.GetApplicationBuilder();
var store = context.ServiceProvider.GetRequiredService<IPermissionStore>();
var xx = AsyncHelper.RunSync(() => store.IsGrantedAsync("AbpTenantManagement.Tenants", "Role", "admin"));
app.UseStaticFiles();
app.UseSwagger();
@ -96,6 +93,8 @@ namespace MicroserviceDemo.TenancyService
app.UseAuthentication();
app.UseMultiTenancy();
app.UseMvcWithDefaultRoute();
}

@ -26,7 +26,7 @@ namespace MicroserviceDemo.TenancyService
.AddDebug()
.AddSerilog(new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.RollingFile("Logs/logs.txt")
.WriteTo.File("Logs/logs.txt")
.CreateLogger()
);

@ -26,7 +26,7 @@ namespace MicroserviceDemo.Web
.AddDebug()
.AddSerilog(new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.RollingFile("Logs/logs.txt")
.WriteTo.File("Logs/logs.txt")
.CreateLogger()
);

@ -14,11 +14,10 @@ namespace Volo.Abp.AspNetCore.MultiTenancy
{
services.Configure<TenantResolveOptions>(options =>
{
options.TenantResolvers.Insert(0, new ClaimsHttpTenantResolveContributer());
options.TenantResolvers.Insert(1, new QueryStringTenantResolveContributer());
options.TenantResolvers.Insert(2, new RouteTenantResolveContributer());
options.TenantResolvers.Insert(3, new HeaderTenantResolveContributer());
options.TenantResolvers.Insert(4, new CookieTenantResolveContributer());
options.TenantResolvers.Add(new QueryStringTenantResolveContributer());
options.TenantResolvers.Add(new RouteTenantResolveContributer());
options.TenantResolvers.Add(new HeaderTenantResolveContributer());
options.TenantResolvers.Add(new CookieTenantResolveContributer());
});
services.AddAssemblyOf<AbpAspNetCoreMultiTenancyModule>();

@ -1,24 +0,0 @@
using System.Linq;
using Microsoft.AspNetCore.Http;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.AspNetCore.MultiTenancy
{
public class ClaimsHttpTenantResolveContributer : HttpTenantResolveContributerBase
{
protected override void ResolveFromHttpContext(ITenantResolveContext context, HttpContext httpContext)
{
if (httpContext.User?.Identity?.IsAuthenticated == true)
{
base.ResolveFromHttpContext(context, httpContext);
context.Handled = true;
}
}
protected override string GetTenantIdOrNameFromHttpContextOrNull(ITenantResolveContext context, HttpContext httpContext)
{
var tenantKey = context.GetAspNetCoreMultiTenancyOptions().TenantKey;
return httpContext.User.Claims.FirstOrDefault(c => c.Type == tenantKey)?.Value;
}
}
}

@ -1,4 +1,5 @@
using Volo.Abp.AspNetCore.MultiTenancy;
using System.Collections.Generic;
using Volo.Abp.AspNetCore.MultiTenancy;
namespace Volo.Abp.MultiTenancy
{
@ -6,7 +7,10 @@ namespace Volo.Abp.MultiTenancy
{
public static void AddDomainTenantResolver(this TenantResolveOptions options, string domainFormat)
{
options.TenantResolvers.Insert(0, new DomainTenantResolveContributer(domainFormat));
options.TenantResolvers.InsertAfter(
r => r is CurrentClaimsPrincipalTenantResolveContributer,
new DomainTenantResolveContributer(domainFormat)
);
}
}
}

@ -9,6 +9,54 @@ namespace System.Collections.Generic
/// </summary>
public static class AbpListExtensions
{
public static int FindIndex<T>(this IList<T> source, Predicate<T> selector)
{
for (var i = 0; i < source.Count; ++i)
{
if (selector(source[i]))
{
return i;
}
}
return -1;
}
public static void AddFirst<T>(this IList<T> source, T item)
{
source.Insert(0, item);
}
public static void AddLast<T>(this IList<T> source, T item)
{
source.Insert(source.Count, item);
}
public static void InsertAfter<T>(this IList<T> source, Predicate<T> selector, T item)
{
var index = source.FindIndex(selector);
if (index < 0)
{
source.AddFirst(item);
return;
}
source.Insert(index + 1, item);
}
public static void InsertBefore<T>(this IList<T> source, Predicate<T> selector, T item)
{
var index = source.FindIndex(selector);
if (index < 0)
{
source.AddLast(item);
return;
}
source.Insert(index, item);
}
public static void MoveItem<T>(this List<T> source, Predicate<T> selector, int targetIndex)
{
if (!targetIndex.IsBetween(0, source.Count - 1))

@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using IdentityServer4.Services;
using Microsoft.Extensions.Logging;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.IdentityServer
{
public class AbpClaimsService : DefaultClaimsService
{
public AbpClaimsService(IProfileService profile, ILogger<DefaultClaimsService> logger)
: base(profile, logger)
{
}
protected override IEnumerable<Claim> GetOptionalClaims(ClaimsPrincipal subject)
{
var tenantClaim = subject.FindFirst(AbpClaimTypes.TenantId);
if (tenantClaim == null)
{
return base.GetOptionalClaims(subject);
}
else
{
return base.GetOptionalClaims(subject).Union(new[] { tenantClaim });
}
}
}
}

@ -1,13 +0,0 @@
using IdentityServer4.Services;
using Microsoft.Extensions.Logging;
namespace Volo.Abp.IdentityServer.AspNetIdentity
{
public class AbpClaimsService : DefaultClaimsService
{
public AbpClaimsService(IProfileService profile, ILogger<DefaultClaimsService> logger)
: base(profile, logger)
{
}
}
}

@ -1,44 +1,41 @@
using System.Threading.Tasks;
using System.Security.Principal;
using IdentityServer4.AspNetIdentity;
using IdentityServer4.Models;
using Microsoft.AspNetCore.Identity;
using Volo.Abp.Identity;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Uow;
namespace Volo.Abp.IdentityServer.AspNetIdentity
{
//TODO: Implement multi-tenancy as like in old ABP
public class AbpProfileService : ProfileService<IdentityUser>
{
private readonly IUnitOfWorkManager _unitOfWorkManager;
private readonly ICurrentTenant _currentTenant;
public AbpProfileService(
IdentityUserManager userManager,
IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
IUnitOfWorkManager unitOfWorkManager)
ICurrentTenant currentTenant)
: base(userManager, claimsFactory)
{
_unitOfWorkManager = unitOfWorkManager;
_currentTenant = currentTenant;
}
[UnitOfWork]
public override async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
using (var uow = _unitOfWorkManager.Begin())
using (_currentTenant.Change(context.Subject.FindTenantId()))
{
await base.GetProfileDataAsync(context);
await uow.CompleteAsync();
}
}
[UnitOfWork]
public override async Task IsActiveAsync(IsActiveContext context)
{
using (var uow = _unitOfWorkManager.Begin())
using (_currentTenant.Change(context.Subject.FindTenantId()))
{
await base.IsActiveAsync(context);
await uow.CompleteAsync();
}
}
}

@ -16,6 +16,7 @@
<ItemGroup>
<ProjectReference Include="..\Volo.Abp.Core\Volo.Abp.Core.csproj" />
<ProjectReference Include="..\Volo.Abp.Data\Volo.Abp.Data.csproj" />
<ProjectReference Include="..\Volo.Abp.Security\Volo.Abp.Security.csproj" />
<ProjectReference Include="..\Volo.Abp.Settings\Volo.Abp.Settings.csproj" />
</ItemGroup>

@ -0,0 +1,21 @@
using System.Linq;
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Security.Claims;
namespace Volo.Abp.MultiTenancy
{
public class CurrentClaimsPrincipalTenantResolveContributer : ITenantResolveContributer
{
public void Resolve(ITenantResolveContext context)
{
var principal = context.ServiceProvider.GetRequiredService<ICurrentPrincipalAccessor>().Principal;
if (principal?.Identity?.IsAuthenticated != true)
{
return;
}
context.TenantIdOrName = principal.Claims.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId)?.Value;
context.Handled = true;
}
}
}

@ -10,7 +10,10 @@ namespace Volo.Abp.MultiTenancy
public TenantResolveOptions()
{
TenantResolvers = new List<ITenantResolveContributer>();
TenantResolvers = new List<ITenantResolveContributer>
{
new CurrentClaimsPrincipalTenantResolveContributer()
};
}
}
}

@ -0,0 +1,67 @@
using System.Linq;
using System.Security.Claims;
using JetBrains.Annotations;
using Volo.Abp;
using Volo.Abp.Security.Claims;
namespace System.Security.Principal
{
public static class AbpClaimsIdentityExtensions
{
public static Guid? FindUserId([NotNull] this ClaimsPrincipal principal)
{
Check.NotNull(principal, nameof(principal));
var userIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId);
if (userIdOrNull == null || userIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return Guid.Parse(userIdOrNull.Value);
}
public static Guid? FindTenantId([NotNull] this ClaimsPrincipal principal)
{
Check.NotNull(principal, nameof(principal));
var tenantIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId);
if (tenantIdOrNull == null || tenantIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return Guid.Parse(tenantIdOrNull.Value);
}
public static Guid? FindUserId([NotNull] this IIdentity identity)
{
Check.NotNull(identity, nameof(identity));
var claimsIdentity = identity as ClaimsIdentity;
var userIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.UserId);
if (userIdOrNull == null || userIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return Guid.Parse(userIdOrNull.Value);
}
public static Guid? FindTenantId([NotNull] this IIdentity identity)
{
Check.NotNull(identity, nameof(identity));
var claimsIdentity = identity as ClaimsIdentity;
var tenantIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.TenantId);
if (tenantIdOrNull == null || tenantIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return Guid.Parse(tenantIdOrNull.Value);
}
}
}

@ -0,0 +1,69 @@
using System.Linq;
using Shouldly;
using Xunit;
namespace System.Collections.Generic
{
public class AbpListExtensions_Tests
{
[Fact]
public void InsertAfter()
{
var list = Enumerable.Range(1, 3).ToList();
list.InsertAfter(i => i == 2, 42);
list.Count.ShouldBe(4);
list[0].ShouldBe(1);
list[1].ShouldBe(2);
list[2].ShouldBe(42);
list[3].ShouldBe(3);
list.InsertAfter(i => i == 3, 43);
list.Count.ShouldBe(5);
list[0].ShouldBe(1);
list[1].ShouldBe(2);
list[2].ShouldBe(42);
list[3].ShouldBe(3);
list[4].ShouldBe(43);
}
[Fact]
public void InsertAfter_Should_Insert_To_First_If_Not_Found()
{
var list = Enumerable.Range(1, 3).ToList();
list.InsertAfter(i => i == 999, 42);
list.Count.ShouldBe(4);
list[0].ShouldBe(42);
list[1].ShouldBe(1);
list[2].ShouldBe(2);
list[3].ShouldBe(3);
}
[Fact]
public void InsertBefore()
{
var list = Enumerable.Range(1, 3).ToList();
list.InsertBefore(i => i == 2, 42);
list.Count.ShouldBe(4);
list[0].ShouldBe(1);
list[1].ShouldBe(42);
list[2].ShouldBe(2);
list[3].ShouldBe(3);
list.InsertBefore(i => i == 1, 43);
list.Count.ShouldBe(5);
list[0].ShouldBe(43);
list[1].ShouldBe(1);
list[2].ShouldBe(42);
list[3].ShouldBe(2);
list[4].ShouldBe(3);
}
}
}
Loading…
Cancel
Save