From 946bd7fb79c49adb3b52657ef28321bff9218d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Halil=20=C4=B0brahim=20Kalkan?= Date: Wed, 7 Mar 2018 10:39:34 +0300 Subject: [PATCH] Set tenantid to the claims on login. --- .../Controllers/MultiTenancyController.cs | 66 +++++++++++++++++++ .../Controllers/MultiTenancyController.cs | 2 +- .../Pages/Account/Login.cshtml | 2 +- .../Pages/Account/Login.cshtml.cs | 15 +++-- .../AbpIdentityServiceCollectionExtensions.cs | 7 +- .../Identity/AbpUserClaimsPrincipalFactory.cs | 38 +++++++++++ .../Volo/Abp/Identity/IdentityRoleManager.cs | 10 ++- .../Volo/Abp/Identity/IdentityUserManager.cs | 11 +++- .../Volo/Abp/Security/Claims/AbpClaimTypes.cs | 5 ++ .../CancellationTokenProviderExtensions.cs | 4 +- 10 files changed, 144 insertions(+), 16 deletions(-) create mode 100644 src/MicroserviceDemo/MicroserviceDemo.AuthServer/Controllers/MultiTenancyController.cs create mode 100644 src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory.cs diff --git a/src/MicroserviceDemo/MicroserviceDemo.AuthServer/Controllers/MultiTenancyController.cs b/src/MicroserviceDemo/MicroserviceDemo.AuthServer/Controllers/MultiTenancyController.cs new file mode 100644 index 0000000000..ac5a453177 --- /dev/null +++ b/src/MicroserviceDemo/MicroserviceDemo.AuthServer/Controllers/MultiTenancyController.cs @@ -0,0 +1,66 @@ +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Options; +using Volo.Abp.AspNetCore.MultiTenancy; +using Volo.Abp.AspNetCore.Mvc; +using Volo.Abp.MultiTenancy; +using Volo.Abp.Ui; + +namespace MicroserviceDemo.AuthServer.Controllers +{ + /* TODO: This is temporary solution to switch tenant. + */ + + public class MultiTenancyController : AbpController + { + private readonly ITenantStore _tenantStore; + private readonly AspNetCoreMultiTenancyOptions _options; + + public MultiTenancyController(ITenantStore tenantStore, IOptions options) + { + _tenantStore = tenantStore; + _options = options.Value; + } + + public async Task SwitchTenant(string tenant = "") + { + if (tenant.IsNullOrEmpty()) + { + HttpContext.Response.Cookies.Delete(_options.TenantKey); + } + else + { + var tenantInfo = await FindTenantAsync(tenant); + if (tenantInfo == null) + { + throw new UserFriendlyException("Unknown tenant: " + tenant); + } + + HttpContext.Response.Cookies.Append( + _options.TenantKey, + tenantInfo.Id.ToString(), + new CookieOptions + { + Expires = DateTimeOffset.Now.AddYears(1) + } + ); + } + + return Redirect("/"); + } + + private async Task FindTenantAsync(string tenantIdOrName) + { + if (Guid.TryParse(tenantIdOrName, out var parsedTenantId)) + { + return await _tenantStore.FindAsync(parsedTenantId); + } + else + { + return await _tenantStore.FindAsync(tenantIdOrName); + } + } + } +} diff --git a/src/MicroserviceDemo/MicroserviceDemo.Web/Controllers/MultiTenancyController.cs b/src/MicroserviceDemo/MicroserviceDemo.Web/Controllers/MultiTenancyController.cs index 9da448980f..e4a8e3a6ad 100644 --- a/src/MicroserviceDemo/MicroserviceDemo.Web/Controllers/MultiTenancyController.cs +++ b/src/MicroserviceDemo/MicroserviceDemo.Web/Controllers/MultiTenancyController.cs @@ -8,7 +8,7 @@ using Volo.Abp.AspNetCore.Mvc; using Volo.Abp.MultiTenancy; using Volo.Abp.Ui; -namespace AbpDesk.Web.Mvc.Controllers +namespace MicroserviceDemo.Web.Controllers { /* TODO: This is temporary solution to switch tenant. */ diff --git a/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml b/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml index 61364f038d..c5aac37e46 100644 --- a/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml +++ b/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml @@ -1,5 +1,5 @@ @page -@model Volo.Abp.Account.Web.Pages.Account.IdsLoginModel +@model Volo.Abp.Account.Web.Pages.Account.IdentityServerLoginModel
@if (Model.EnableLocalLogin) diff --git a/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml.cs b/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml.cs index 8626609524..096fba87d9 100644 --- a/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml.cs +++ b/src/Volo.Abp.Account.Web.IdentityServer/Pages/Account/Login.cshtml.cs @@ -25,7 +25,7 @@ using Volo.Abp.Uow; namespace Volo.Abp.Account.Web.Pages.Account { //TODO: Inherit from LoginModel of Account.Web project. We should design it as extensible. - public class IdsLoginModel : AccountModelBase + public class IdentityServerLoginModel : AccountModelBase { [HiddenInput] [BindProperty(SupportsGet = true)] @@ -55,7 +55,7 @@ namespace Volo.Abp.Account.Web.Pages.Account private readonly IClientStore _clientStore; private readonly IEventService _identityServerEvents; - public IdsLoginModel( + public IdentityServerLoginModel( SignInManager signInManager, IdentityUserManager userManager, IIdentityServerInteractionService interaction, @@ -83,17 +83,20 @@ namespace Volo.Abp.Account.Web.Pages.Account { LoginInput.UserNameOrEmailAddress = context.LoginHint; - //TODO: !!! Always exchanging tenant id, not name! + //TODO: Reference AspNetCore MultiTenancy module and use options to get the tenant key! var tenant = context.Parameters[TenantResolverConsts.DefaultTenantKey]; if (tenant.IsNullOrEmpty()) { - Response.Cookies.Delete(TenantResolverConsts.DefaultTenantKey); - CurrentTenant.Change(null); + if (Request.Cookies.ContainsKey(TenantResolverConsts.DefaultTenantKey)) + { + CurrentTenant.Change(null); + Response.Cookies.Delete(TenantResolverConsts.DefaultTenantKey); + } } else { - Response.Cookies.Append(TenantResolverConsts.DefaultTenantKey, tenant); CurrentTenant.Change(Guid.Parse(tenant)); + Response.Cookies.Append(TenantResolverConsts.DefaultTenantKey, tenant); } } diff --git a/src/Volo.Abp.Identity.Domain/Microsoft/Extensions/DependencyInjection/AbpIdentityServiceCollectionExtensions.cs b/src/Volo.Abp.Identity.Domain/Microsoft/Extensions/DependencyInjection/AbpIdentityServiceCollectionExtensions.cs index d9ee732225..5a59253205 100644 --- a/src/Volo.Abp.Identity.Domain/Microsoft/Extensions/DependencyInjection/AbpIdentityServiceCollectionExtensions.cs +++ b/src/Volo.Abp.Identity.Domain/Microsoft/Extensions/DependencyInjection/AbpIdentityServiceCollectionExtensions.cs @@ -5,8 +5,6 @@ using Volo.Abp.Identity; namespace Microsoft.Extensions.DependencyInjection { - //TODO: AspNetUserManager overrides CancellationToken so we can make same functionality available! - public static class AbpIdentityServiceCollectionExtensions { public static IdentityBuilder AddAbpIdentity(this IServiceCollection services) @@ -36,9 +34,10 @@ namespace Microsoft.Extensions.DependencyInjection //AbpRoleStore services.TryAddScoped(); services.TryAddScoped(typeof(IRoleStore), provider => provider.GetService(typeof(IdentityRoleStore))); - + return services.AddIdentity(setupAction) - .AddDefaultTokenProviders(); + .AddDefaultTokenProviders() + .AddClaimsPrincipalFactory(); //return services.AddIdentityCore(setupAction); } } diff --git a/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory.cs b/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory.cs new file mode 100644 index 0000000000..10e7ae3c3f --- /dev/null +++ b/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/AbpUserClaimsPrincipalFactory.cs @@ -0,0 +1,38 @@ +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Identity; +using Microsoft.Extensions.Options; +using Volo.Abp.DependencyInjection; +using Volo.Abp.Security.Claims; +using Volo.Abp.Uow; + +namespace Volo.Abp.Identity +{ + public class AbpUserClaimsPrincipalFactory : UserClaimsPrincipalFactory, ITransientDependency + { + public AbpUserClaimsPrincipalFactory( + UserManager userManager, + RoleManager roleManager, + IOptions options) + : base( + userManager, + roleManager, + options) + { + } + + [UnitOfWork] + public override async Task CreateAsync(IdentityUser user) + { + var principal = await base.CreateAsync(user); + + if (user.TenantId.HasValue) + { + principal.Identities.First().AddClaim(new Claim(AbpClaimTypes.TenantId, user.TenantId.ToString())); + } + + return principal; + } + } +} diff --git a/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs b/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs index d1da955666..e59a056566 100644 --- a/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs +++ b/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityRoleManager.cs @@ -1,21 +1,28 @@ using System; using System.Collections.Generic; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.Logging; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Services; +using Volo.Abp.Threading; namespace Volo.Abp.Identity { public class IdentityRoleManager : RoleManager, IDomainService { + protected override CancellationToken CancellationToken => _cancellationTokenProvider.Token; + + private readonly ICancellationTokenProvider _cancellationTokenProvider; + public IdentityRoleManager( IdentityRoleStore store, IEnumerable> roleValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, - ILogger logger) + ILogger logger, + ICancellationTokenProvider cancellationTokenProvider) : base( store, roleValidators, @@ -23,6 +30,7 @@ namespace Volo.Abp.Identity errors, logger) { + _cancellationTokenProvider = cancellationTokenProvider; } public async Task GetByIdAsync(Guid id) diff --git a/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs b/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs index 4bfb31154c..dc4b2fe4b2 100644 --- a/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs +++ b/src/Volo.Abp.Identity.Domain/Volo/Abp/Identity/IdentityUserManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; using JetBrains.Annotations; using Microsoft.AspNetCore.Identity; @@ -8,11 +9,16 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Volo.Abp.Domain.Entities; using Volo.Abp.Domain.Services; +using Volo.Abp.Threading; namespace Volo.Abp.Identity { public class IdentityUserManager : UserManager, IDomainService { + protected override CancellationToken CancellationToken => _cancellationTokenProvider.Token; + + private readonly ICancellationTokenProvider _cancellationTokenProvider; + public IdentityUserManager( IdentityUserStore store, IOptions optionsAccessor, @@ -21,7 +27,8 @@ namespace Volo.Abp.Identity IEnumerable> passwordValidators, ILookupNormalizer keyNormalizer, IdentityErrorDescriber errors, IServiceProvider services, - ILogger logger) + ILogger logger, + ICancellationTokenProvider cancellationTokenProvider) : base( store, optionsAccessor, @@ -33,7 +40,7 @@ namespace Volo.Abp.Identity services, logger) { - + _cancellationTokenProvider = cancellationTokenProvider; } public async Task GetByIdAsync(Guid id) diff --git a/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs b/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs index be363b8df4..0edabc957d 100644 --- a/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs +++ b/src/Volo.Abp.Security/Volo/Abp/Security/Claims/AbpClaimTypes.cs @@ -42,5 +42,10 @@ namespace Volo.Abp.Security.Claims /// Default: "phone_number_verified". /// public static string PhoneNumberVerified { get; set; } = "phone_number_verified"; + + /// + /// Default: "phone_number_verified". + /// + public static string TenantId { get; set; } = "tenantid"; } } diff --git a/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderExtensions.cs b/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderExtensions.cs index b1ad755db2..bdae019b66 100644 --- a/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderExtensions.cs +++ b/src/Volo.Abp.Threading/Volo/Abp/Threading/CancellationTokenProviderExtensions.cs @@ -6,7 +6,9 @@ namespace Volo.Abp.Threading { public static CancellationToken FallbackToProvider(this ICancellationTokenProvider provider, CancellationToken prefferedValue = default) { - return prefferedValue == default ? provider.Token : prefferedValue; + return prefferedValue == default || prefferedValue == CancellationToken.None + ? provider.Token + : prefferedValue; } } } \ No newline at end of file