Hi,
I have implemented a custom ConnectionStringResolver class but the ICurrentTenant implementation does not resolve to the current logged in tenant. However I can't even get ABP's solutions to run even if I exclude this resolver class. At this point I have grown very tired of getting any sample projects to work to show support when I generate them using ABP tools, so I'll just give you the generated solution and you can figure out how to get it to run first and then we can have a discussion about the CurrentTenant issue I am having.
Can't waste yet another day trying to get a stock generated ABP solution to run
Steps to Reproduce:
- Get stock ABP generated solution to compile/run (Good luck as I've spend 2 days attempting to get it generated and to even work)
- Login in as host admin and create a Tenant of 'KENCO' with a non-shared DB (tenant has their own db)
- Login in as the KENCO tenant admin
- Monitor the debug messages I've added to the CFDMultiTenantConnectionStringResolver.cs class
- Observe the CurrentTenant variable still being set to null
- I've not been able to verify this test project will reproduce the issue I am experiencing as I've stated above I can't even get the ABP tool generated solution to compile/run
https://github.com/spospisil/ABPSampleTenant.git
57 Answer(s)
-
0
hi
Please make your GitHub repos PRIVATE.
-
0
I can build your project via
dotnet build
command% dotnet build Restore complete (0.2s) Test.Domain.Shared succeeded (0.1s) → ABPSampleTenant/src/Test.Domain.Shared/bin/Debug/net9.0/Test.Domain.Shared.dll Test.Domain succeeded (0.1s) → ABPSampleTenant/src/Test.Domain/bin/Debug/net9.0/Test.Domain.dll Test.EntityFrameworkCore succeeded (0.1s) → ABPSampleTenant/src/Test.EntityFrameworkCore/bin/Debug/net9.0/Test.EntityFrameworkCore.dll Test.AuthServer succeeded (2.6s) → bin/Debug/net9.0/Test.AuthServer.dll Build succeeded in 3.7s maliming@malim Test.AuthServer %
-
0
If your
Entity
has noIMultiTenant
, abp will switch to host(tenant will null
) If yourDbContext
hasIgnoreMultiTenancyAttribute
, abp will switch to host(tenant will null
) -
0
So, neither one of those things is true as you can see in the demo project!
-
0
-
0
Ok, thanks for getting the base project solution working...funny how myself and a coworker were both unable to get it to compile and run in Visual Studio.
Let me now take the working base project and try to reproduce the issue I am experiencing in my actual project.
-
0
ok. I just open it in vscode and run it
dotnet run
-
0
Well, since you can see that I specified Visual Studio 2022 vs vscode in the initial creation of the ticket, I'm not sure why you would use vscode. What's the point of asking out environment tools if you're just going to use something else to attempt to reproduce the issue. Doesn't really make sense does it
-
0
hi
End your VS studio. and run
dotnet build /graphbuild
to build your project. then open it in VS Studio again. -
0
What are the other possibilities why ICurrentTenant would not resolve to the current tenant for my abpmodule? I can't reproduce the issue and sharing the code where it's happening is not an option. I've spent over a week trying to figure this out with no success.
-
0
I don't have many ideas.
I ran your project, and it works.
https://abp.io/support/questions/9350/Custom-ConnectionStringResolver-Issue#answer-3a1a07c1-789d-d7e0-e1c2-2bab8465974e
-
0
-
0
-
0
Hi,
See my below code for the login. I have verified at least with my custom login page that the resolver exists as it should. Is there something I am doing wrong with replacing the login logic?
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Owl.reCAPTCHA; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Account; using Volo.Abp.Account.ExternalProviders; using Volo.Abp.Account.Public.Web; using Volo.Abp.Account.Public.Web.Pages.Account; using Volo.Abp.Account.Public.Web.Security.Recaptcha; using Volo.Abp.Account.Security.Recaptcha; using Volo.Abp.Account.Settings; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Identity.Settings; using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; using Volo.Abp.Security.Claims; using Volo.Abp.Settings; using Volo.Abp.Uow; using Volo.Abp.Validation; using Volo.Saas.Tenants; using IdentityUser = Volo.Abp.Identity.IdentityUser; namespace CFData.Structure.Web.Pages.Account; [DisableAuditing] [Dependency(ReplaceServices = true)] [ExposeServices(typeof(MyLoginModel), typeof(LoginModel))] public class MyLoginModel : LoginModel { [Inject] public ICurrentTenantAccessor CurrentTenantAccessor { get; set; } [Inject] public ITenantStore TenantStore { get; set; } private readonly ITenantRepository _tenantRepository; protected IDataFilter DataFilter { get; } public MyLoginModel( IAuthenticationSchemeProvider schemeProvider, IOptions<AbpAccountOptions> accountOptions, IAbpRecaptchaValidatorFactory recaptchaValidatorFactory, IAccountExternalProviderAppService accountExternalProviderAppService, ICurrentPrincipalAccessor currentPrincipalAccessor, IOptions<IdentityOptions> identityOptions, IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions, ITenantRepository tenantRepository, DataFilter dataFilter) : base( schemeProvider, accountOptions, recaptchaValidatorFactory, accountExternalProviderAppService, currentPrincipalAccessor, identityOptions, reCaptchaOptions) { //ReCaptchaOptions = reCaptchaOptions; _tenantRepository = tenantRepository; DataFilter = dataFilter; } public override async Task<IActionResult> OnGetAsync() { return await base.OnGetAsync(); } [UnitOfWork] //TODO: Will be removed when we implement action filter public override async Task<IActionResult> OnPostAsync(string action) { try { await ReCaptchaVerification(); } catch (UserFriendlyException e) { if (e is ScoreBelowThresholdException) { var onScoreBelowThresholdResult = OnRecaptchaScoreBelowThreshold(); if (onScoreBelowThresholdResult != null) { return await onScoreBelowThresholdResult; } } Alerts.Danger(GetLocalizeExceptionMessage(e)); return Page(); } //ValidateModel(); await IdentityOptions.SetAsync(); var localLoginResult = await CheckLocalLoginAsync(); if (localLoginResult != null) { return localLoginResult; } IsSelfRegistrationEnabled = false; //await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled); await ReplaceEmailToUsernameOfInputIfNeeds(); var getUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (getUser == null) { Alerts.Danger("User does not exist"); return Page(); } TenantConfiguration tenant = null; if (getUser.TenantId is not null) { tenant = await TenantStore.FindAsync((Guid)getUser.TenantId); CurrentTenantAccessor.Current = new BasicTenantInfo( tenant?.Id, tenant?.Name); } IsLinkLogin = await VerifyLinkTokenAsync(); var result = new Microsoft.AspNetCore.Identity.SignInResult(); using (DataFilter.Disable<IMultiTenant>()) { result = await SignInManager.PasswordSignInAsync( LoginInput.UserNameOrEmailAddress, LoginInput.Password, LoginInput.RememberMe, true); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), UserName = LoginInput.UserNameOrEmailAddress }); } if (result.RequiresTwoFactor) { return RedirectToPage("./SendSecurityCode", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, rememberMe = LoginInput.RememberMe, linkUserId = LinkUserId, linkTenantId = LinkTenantId, linkToken = LinkToken }); } if (result.IsLockedOut) { var lockedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress); await StoreLockedUser(lockedUser); return RedirectToPage("./LockedOut", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } if (result.IsNotAllowed) { var notAllowedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (!await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) { Alerts.Danger(L["InvalidUserNameOrPassword"]); return Page(); } if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser)) { await StoreChangePasswordUser(notAllowedUser); return RedirectToPage("./ChangePassword", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, RememberMe = LoginInput.RememberMe }); } if (notAllowedUser.IsActive) { await StoreConfirmUser(notAllowedUser); return RedirectToPage("./ConfirmUser", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } Alerts.Danger(L["LoginIsNotAllowed"]); return Page(); } if (!result.Succeeded) { Alerts.Danger(L["InvalidUserNameOrPassword"]); return Page(); } var user = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (IsLinkLogin) { using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(user))) { await IdentityLinkUserAppService.LinkAsync(new LinkUserInput { UserId = LinkUserId.Value, TenantId = LinkTenantId, Token = LinkToken }); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentityProSecurityLogActionConsts.LinkUser, UserName = user.UserName, ExtraProperties = { { IdentityProSecurityLogActionConsts.LinkTargetTenantId, LinkTenantId }, { IdentityProSecurityLogActionConsts.LinkTargetUserId, LinkUserId } } }); using (CurrentTenant.Change(LinkTenantId)) { var targetUser = await UserManager.GetByIdAsync(LinkUserId.Value); using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(targetUser))) { await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentityProSecurityLogActionConsts.LinkUser, UserName = targetUser.UserName, ExtraProperties = { { IdentityProSecurityLogActionConsts.LinkTargetTenantId, targetUser.TenantId }, { IdentityProSecurityLogActionConsts.LinkTargetUserId, targetUser.Id } } }); } } return RedirectToPage("./LinkLogged", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, targetLinkUserId = LinkUserId, targetLinkTenantId = LinkTenantId }); } } // Clear the dynamic claims cache. await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } protected override async Task ReplaceEmailToUsernameOfInputIfNeeds() { if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress)) { return; } var userByUsername = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (userByUsername != null) { return; } var userByEmail = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (userByEmail == null) { return; } LoginInput.UserNameOrEmailAddress = userByEmail.UserName; } protected override async Task<IdentityUser> GetIdentityUser(string userNameOrEmailAddress) { IdentityUser identityUser = null; using (CurrentTenant.Change(null)) { identityUser = await base.UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress); if (identityUser == null) { identityUser = await base.UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); } } IdentityUser user = identityUser; Debug.Assert(user != null, "user != null"); return user; } protected virtual async Task<IdentityUser> FindUserAsync(string uniqueUserNameOrEmailAddress) { IdentityUser user = null; using (CurrentTenant.Change(null)) { user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); if (user != null) { return user; } } foreach (var tenant in await _tenantRepository.GetListAsync()) { using (CurrentTenant.Change(tenant.Id)) { user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); if (user != null) { return user; } } } return null; } }
-
0
hi
Do you get any errors/exceptions with your custom login page?
Thanks.
-
0
Can you tell me the things I need to check? Again the issue seems to be in any abp layered modules that I create the currenttenant is always null/blank even though I'm logged in as the tenant? Since I can't reproduce this issue I need guidance from ABP in order to check what might be wrong.
It's been 3 weeks of solely focused on this and I still have no resolution.
-
0
hi
I create the currenttenant is always null/blank even though I'm logged in as the tenant?
What are the claims of your current user?
You can inject the
ICurrentUser
and call theGetAllClaims
method. -
0
Hi,
Here are the claims associated with the current logged in tenant user
[09:55:10 FTL] Claim: email_verified Value: True [09:55:10 FTL] Claim: phone_number_verified Value: False [09:55:10 FTL] Claim: editionid Value: 3a1a4870-79bf-c38d-1e33-3c5a09fffd1e [09:55:10 FTL] Claim: client_id Value: Structure_Blazor [09:55:10 FTL] Claim: oi_tkn_id Value: 3a1a4c89-cfb7-9302-f739-76a2df5e99ea
The following shows the current user, current user TenantId (which is correct), but then the CurrentTenantId is null and the IsAvailable is False.
[09:55:10 FTL] Current User: KENCO_admin CurrentUserTenantId: 3a1a4978-6b4a-2eaa-01c7-d5abd68df27f [09:55:10 FTL] Current ConnectionStringName: Company CurrentTenantId: IsAvailable: False [09:55:10 FTL] Connection String Resolved: Server=postgresql;Database=KENCO;Port=5432;User Id=sc_kenco_dba_3a1a49786b3b044130998ef1dd29e3a7;Password=a334!1Sz[-yJ;Ssl Mode=allow;
-
0
Hi,
After realizing that I was missing some code in our AuthServer Module code I now get this but as you can see the Current TenantId is still not getting set.
[10:44:13 FTL] Claim: iss Value: https://localhost.structurecloud.com/Auth [10:44:13 FTL] Claim: exp Value: 1749037452 [10:44:13 FTL] Claim: iat Value: 1749033852 [10:44:13 FTL] Claim: aud Value: Structure [10:44:13 FTL] Claim: scope Value: openid profile Structure roles email phone [10:44:13 FTL] Claim: jti Value: 5c8a9744-9af6-4387-a1d5-deb62c48269b [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier Value: 3a1a4978-8cfc-22db-b223-931eeb528231 [10:44:13 FTL] Claim: preferred_username Value: KENCO_admin [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress Value: spospisil109@gmail.com [10:44:13 FTL] Claim: tenantid Value: 3a1a4978-6b4a-2eaa-01c7-d5abd68df27f [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname Value: Kenco Admin [10:44:13 FTL] Claim: phone_number_verified Value: False [10:44:13 FTL] Claim: email_verified Value: True [10:44:13 FTL] Claim: session_id Value: 673ea7fd-60d4-4e55-8451-84247357b6a9 [10:44:13 FTL] Claim: http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name Value: KENCO_admin [10:44:13 FTL] Claim: editionid Value: 3a1a4870-79bf-c38d-1e33-3c5a09fffd1e [10:44:13 FTL] Claim: BrowserInfo Value: 13926292-bf7f-4e71-9684-c6fc7e28a316 [10:44:13 FTL] Claim: oi_prst Value: Structure_Blazor [10:44:13 FTL] Claim: oi_au_id Value: 3a1a497a-9a71-380a-0d63-c9be9944c3cf [10:44:13 FTL] Claim: client_id Value: Structure_Blazor [10:44:13 FTL] Claim: oi_tkn_id Value: 3a1a4cb7-747a-af97-ba5f-7c1531707b55 [10:44:13 FTL] Current User: KENCO_admin CurrentUserTenantId: 3a1a4978-6b4a-2eaa-01c7-d5abd68df27f [10:44:13 FTL] Current ConnectionStringName: Company CurrentTenantId: IsAvailable: False [10:44:13 FTL] Connection String Resolved: Server=postgresql;Database=KENCO;Port=5432;User Id=sc_kenco_dba_3a1a49786b3b044130998ef1dd29e3a7;Password=a334!1Sz[-yJ;Ssl Mode=allow;
Here is the code in my connection string resolver class.
[Dependency(ReplaceServices = true)] [ExposeServices(typeof(IConnectionStringResolver), typeof(MultiTenantConnectionStringResolver))] public class SWMultiTenantConnectionStringResolver : MultiTenantConnectionStringResolver { private readonly ICurrentTenant _currentTenant; private readonly ICurrentUser _currentUser; private readonly IServiceProvider _serviceProvider; private readonly ILogger<SWMultiTenantConnectionStringResolver> _logger; public SWMultiTenantConnectionStringResolver( IOptionsMonitor<AbpDbConnectionOptions> options, ICurrentTenant currentTenant, IServiceProvider serviceProvider, ICurrentUser currentUser, ILogger<SWMultiTenantConnectionStringResolver> logger) : base(options, currentTenant, serviceProvider) { _currentTenant = currentTenant; _currentUser = currentUser; _serviceProvider = serviceProvider; _logger = logger; } public override async Task<string> ResolveAsync(string? connectionStringName = null) { string connectionstring = ""; var claims = _currentUser.GetAllClaims().ToList(); foreach (var claim in claims) { _logger.LogCritical($"Claim: {claim.Type} Value: {claim.Value}"); } _logger.LogCritical($"Current User: {_currentUser.UserName} CurrentUserTenantId: {_currentUser.TenantId}"); _logger.LogCritical($"Current ConnectionStringName: {connectionStringName} CurrentTenantId: {_currentTenant.Id} IsAvailable: {_currentTenant.IsAvailable}"); //Force Current Tenant to write to Host DB for the below functions switch (connectionStringName) { case "Default": case LanguageManagementDbProperties.ConnectionStringName: case AbpFeatureManagementDbProperties.ConnectionStringName: case AbpPermissionManagementDbProperties.ConnectionStringName: case AbpOpenIddictDbProperties.ConnectionStringName: case AbpBackgroundJobsDbProperties.ConnectionStringName: case AbpBlobStoringDatabaseDbProperties.ConnectionStringName: case AbpIdentityDbProperties.ConnectionStringName: case AbpCmsKitDbProperties.ConnectionStringName: case AbpSettingManagementDbProperties.ConnectionStringName: case SaasDbProperties.ConnectionStringName: case ChatDbProperties.ConnectionStringName: case TextTemplateManagementDbProperties.ConnectionStringName: case PaymentDbProperties.ConnectionStringName: case TemplateDbProperties.ConnectionStringName: case SecurityDbProperties.ConnectionStringName: case LexiconDbProperties.ConnectionStringName: case LicensingDbProperties.ConnectionStringName: using (_currentTenant.Change(null)) { connectionstring = await base.ResolveAsync(connectionStringName); _logger.LogCritical($"Connection String Resolved: {connectionstring}"); return await Task.FromResult(connectionstring); } default: if (_currentTenant.Id is null || _currentUser.TenantId is not null) { using (_currentTenant.Change(_currentUser.TenantId)) { connectionstring = await base.ResolveAsync(connectionStringName); } } else { connectionstring = await base.ResolveAsync(connectionStringName); } _logger.LogCritical($"Connection String Resolved: {connectionstring}"); return await Task.FromResult(connectionstring); } }
Here is my code in my auth server module
private static void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration) { context.Services.AddAuthentication() .AddGoogle(GoogleDefaults.AuthenticationScheme, _ => { }) .WithDynamicOptions<GoogleOptions, GoogleHandler>( GoogleDefaults.AuthenticationScheme, options => { options.WithProperty(x => x.ClientId); options.WithProperty(x => x.ClientSecret, isSecret: true); } ) .AddMicrosoftAccount(MicrosoftAccountDefaults.AuthenticationScheme, options => { //Personal Microsoft accounts as an example. options.AuthorizationEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize"; options.TokenEndpoint = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"; }) .WithDynamicOptions<MicrosoftAccountOptions, MicrosoftAccountHandler>( MicrosoftAccountDefaults.AuthenticationScheme, options => { options.WithProperty(x => x.ClientId); options.WithProperty(x => x.ClientSecret, isSecret: true); } ) .AddTwitter(TwitterDefaults.AuthenticationScheme, options => options.RetrieveUserDetails = true) .WithDynamicOptions<TwitterOptions, TwitterHandler>( TwitterDefaults.AuthenticationScheme, options => { options.WithProperty(x => x.ConsumerKey); options.WithProperty(x => x.ConsumerSecret, isSecret: true); } ) .AddJwtBearer(options => { options.Authority = configuration["AuthServer:Authority"]; options.RequireHttpsMetadata = Convert.ToBoolean(configuration["AuthServer:RequireHttpsMetadata"]); options.Audience = "Structure"; }); }
-
0
hi
Try using
Change
method instead of setCurrentTenantAccessor.Current
TenantConfiguration tenant = null; if (getUser.TenantId is not null) { tenant = await TenantStore.FindAsync((Guid)getUser.TenantId); CurrentTenantAccessor.Current = new BasicTenantInfo( tenant?.Id, tenant?.Name); }
using (CurrentTenant.Change(tenant?.Id)) { // Add all the code following this to this scope }
-
0
Exactly where do I make your suggested change and will this set the current tenant correctly so all queries I’ll to the db will be filtered by that tenant automatically?
-
0
Exactly where do I make your suggested change and will this set the current tenant correctly so all queries I’ll to the db will be filtered by that tenant automatically?
I have found that everywhere in my blazor wasm app the current tenant is not being set correctly. No idea why.
-
0
hi
Tenant will work as expected when tenant claim is set correctly.
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Http.Extensions; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Owl.reCAPTCHA; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Diagnostics; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Volo.Abp; using Volo.Abp.Account; using Volo.Abp.Account.ExternalProviders; using Volo.Abp.Account.Public.Web; using Volo.Abp.Account.Public.Web.Pages.Account; using Volo.Abp.Account.Public.Web.Security.Recaptcha; using Volo.Abp.Account.Security.Recaptcha; using Volo.Abp.Account.Settings; using Volo.Abp.Auditing; using Volo.Abp.Data; using Volo.Abp.DependencyInjection; using Volo.Abp.Identity; using Volo.Abp.Identity.AspNetCore; using Volo.Abp.Identity.Settings; using Volo.Abp.MultiTenancy; using Volo.Abp.Reflection; using Volo.Abp.Security.Claims; using Volo.Abp.Settings; using Volo.Abp.Uow; using Volo.Abp.Validation; using Volo.Saas.Tenants; using IdentityUser = Volo.Abp.Identity.IdentityUser; namespace CFData.Structure.Web.Pages.Account; [DisableAuditing] [Dependency(ReplaceServices = true)] [ExposeServices(typeof(MyLoginModel), typeof(LoginModel))] public class MyLoginModel : LoginModel { [Inject] public ICurrentTenantAccessor CurrentTenantAccessor { get; set; } [Inject] public ITenantStore TenantStore { get; set; } private readonly ITenantRepository _tenantRepository; protected IDataFilter DataFilter { get; } public MyLoginModel( IAuthenticationSchemeProvider schemeProvider, IOptions accountOptions, IAbpRecaptchaValidatorFactory recaptchaValidatorFactory, IAccountExternalProviderAppService accountExternalProviderAppService, ICurrentPrincipalAccessor currentPrincipalAccessor, IOptions identityOptions, IOptionsSnapshot reCaptchaOptions, ITenantRepository tenantRepository, DataFilter dataFilter) : base( schemeProvider, accountOptions, recaptchaValidatorFactory, accountExternalProviderAppService, currentPrincipalAccessor, identityOptions, reCaptchaOptions) { //ReCaptchaOptions = reCaptchaOptions; _tenantRepository = tenantRepository; DataFilter = dataFilter; } public override async Task OnGetAsync() { return await base.OnGetAsync(); } [UnitOfWork] //TODO: Will be removed when we implement action filter public override async Task OnPostAsync(string action) { try { await ReCaptchaVerification(); } catch (UserFriendlyException e) { if (e is ScoreBelowThresholdException) { var onScoreBelowThresholdResult = OnRecaptchaScoreBelowThreshold(); if (onScoreBelowThresholdResult != null) { return await onScoreBelowThresholdResult; } } Alerts.Danger(GetLocalizeExceptionMessage(e)); return Page(); } //ValidateModel(); await IdentityOptions.SetAsync(); var localLoginResult = await CheckLocalLoginAsync(); if (localLoginResult != null) { return localLoginResult; } IsSelfRegistrationEnabled = false; //await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled); await ReplaceEmailToUsernameOfInputIfNeeds(); var getUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (getUser == null) { Alerts.Danger("User does not exist"); return Page(); } TenantConfiguration tenant = null; if (getUser.TenantId is not null) { tenant = await TenantStore.FindAsync((Guid)getUser.TenantId); } using (CurrentTenant.Change(tenant?.Id)) { IsLinkLogin = await VerifyLinkTokenAsync(); var result = new Microsoft.AspNetCore.Identity.SignInResult(); using (DataFilter.Disable()) { result = await SignInManager.PasswordSignInAsync( LoginInput.UserNameOrEmailAddress, LoginInput.Password, LoginInput.RememberMe, true); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = result.ToIdentitySecurityLogAction(), UserName = LoginInput.UserNameOrEmailAddress }); } if (result.RequiresTwoFactor) { return RedirectToPage("./SendSecurityCode", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, rememberMe = LoginInput.RememberMe, linkUserId = LinkUserId, linkTenantId = LinkTenantId, linkToken = LinkToken }); } if (result.IsLockedOut) { var lockedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress); await StoreLockedUser(lockedUser); return RedirectToPage("./LockedOut", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } if (result.IsNotAllowed) { var notAllowedUser = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (!await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password)) { Alerts.Danger(L["InvalidUserNameOrPassword"]); return Page(); } if (notAllowedUser.ShouldChangePasswordOnNextLogin || await UserManager.ShouldPeriodicallyChangePasswordAsync(notAllowedUser)) { await StoreChangePasswordUser(notAllowedUser); return RedirectToPage("./ChangePassword", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, RememberMe = LoginInput.RememberMe }); } if (notAllowedUser.IsActive) { await StoreConfirmUser(notAllowedUser); return RedirectToPage("./ConfirmUser", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash }); } Alerts.Danger(L["LoginIsNotAllowed"]); return Page(); } if (!result.Succeeded) { Alerts.Danger(L["InvalidUserNameOrPassword"]); return Page(); } var user = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (IsLinkLogin) { using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(user))) { await IdentityLinkUserAppService.LinkAsync(new LinkUserInput { UserId = LinkUserId.Value, TenantId = LinkTenantId, Token = LinkToken }); await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentityProSecurityLogActionConsts.LinkUser, UserName = user.UserName, ExtraProperties = { { IdentityProSecurityLogActionConsts.LinkTargetTenantId, LinkTenantId }, { IdentityProSecurityLogActionConsts.LinkTargetUserId, LinkUserId } } }); using (CurrentTenant.Change(LinkTenantId)) { var targetUser = await UserManager.GetByIdAsync(LinkUserId.Value); using (CurrentPrincipalAccessor.Change(await SignInManager.CreateUserPrincipalAsync(targetUser))) { await IdentitySecurityLogManager.SaveAsync(new IdentitySecurityLogContext { Identity = IdentitySecurityLogIdentityConsts.Identity, Action = IdentityProSecurityLogActionConsts.LinkUser, UserName = targetUser.UserName, ExtraProperties = { { IdentityProSecurityLogActionConsts.LinkTargetTenantId, targetUser.TenantId }, { IdentityProSecurityLogActionConsts.LinkTargetUserId, targetUser.Id } } }); } } return RedirectToPage("./LinkLogged", new { returnUrl = ReturnUrl, returnUrlHash = ReturnUrlHash, targetLinkUserId = LinkUserId, targetLinkTenantId = LinkTenantId }); } } // Clear the dynamic claims cache. await IdentityDynamicClaimsPrincipalContributorCache.ClearAsync(user.Id, user.TenantId); return await RedirectSafelyAsync(ReturnUrl, ReturnUrlHash); } } protected override async Task ReplaceEmailToUsernameOfInputIfNeeds() { if (!ValidationHelper.IsValidEmailAddress(LoginInput.UserNameOrEmailAddress)) { return; } var userByUsername = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (userByUsername != null) { return; } var userByEmail = await FindUserAsync(LoginInput.UserNameOrEmailAddress); if (userByEmail == null) { return; } LoginInput.UserNameOrEmailAddress = userByEmail.UserName; } protected override async Task GetIdentityUser(string userNameOrEmailAddress) { IdentityUser identityUser = null; using (CurrentTenant.Change(null)) { identityUser = await base.UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress); if (identityUser == null) { identityUser = await base.UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); } } IdentityUser user = identityUser; Debug.Assert(user != null, "user != null"); return user; } protected virtual async Task FindUserAsync(string uniqueUserNameOrEmailAddress) { IdentityUser user = null; using (CurrentTenant.Change(null)) { user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); if (user != null) { return user; } } foreach (var tenant in await _tenantRepository.GetListAsync()) { using (CurrentTenant.Change(tenant.Id)) { user = await UserManager.FindByNameAsync(LoginInput.UserNameOrEmailAddress) ?? await UserManager.FindByEmailAsync(LoginInput.UserNameOrEmailAddress); if (user != null) { return user; } } } return null; } }
-
0
This still has not worked. I've given you access to our forked source code where this an issue. The branch name 'Integration-BugFixes' in this forked report is the branch that has the code you want to look at.
Thanks.
-
0
hi
Can you share the steps to reproduce your problem with the
Integration-BugFixes
code?Thanks.