Activities of "cbogner85"

Same here. Hopefully this gets fixed soon.

Hi @maliming!

Thank you for pointing me to the right direction.

I found out that the issue occurs in UserLookupService > FindByUserId. It seems that sometimes the tenant filter is not working correctly, although I changed the tenant.

I worked around by disabling multi-tenancy filter for the user lookup:

       // Fix CmsUser Insert
        TUser localUser;

        using (_dataFilter.Disable<IMultiTenant>())
        {
            localUser = await _userRepository.FindAsync(id, cancellationToken: cancellationToken);
        }
          

Seems a bit quick and dirty, but on the other hand, the id is unique anyway, so I don't see a problem doing the search globally. Since that change the issue is gone.

Therefore I'll close this issue.

hi @maliming,

I can't change to the host, as it would only find host users. I don't want to disable multi-tenancy, I absolutely need all items to be multi-tenant. I only want to let the users login from a unified login page without having to select the tenant from tenantbox or having different subdomains for each tenant.

the function of tenantId = await _extendedOrganizationUnitAppService.GetTenantByUsername(LoginInput.UserNameOrEmailAddress); is quite simple:

  public async Task<Guid?> GetTenantByUsername(string userName)
  {
            var user = await _identityUserRepository.FindByNormalizedUserNameAsync(userName.ToUpper());
            if (user != null)
            {
                return user.TenantId;
            }
            else
            {
                return null;
            }
            
}

Every few days one I got the issue again after a couple of days without the issue. It's really strange. A user can't login from the unified login page, because a duplicate Id insertion into table CmsUsers is tried and the result is an error 500. When I change his password and try to login, I can reproduce the issue. When I login for one time using the tenant specific URL (with subdomain), it works and after that, the unified login works again, too. Therefore I think that the tenant (null, from the unified page) is stored somewhere and is used during CmsUser search, although I changed it in my custom login method... of course I tried in private mode of the browser to ensure the tenant isn't stored somewhere in the cookies.

Where does CmsUser search/ insert during login process takes place? Could you point me to the source file? Maybe overriding it to customize it to my needs is easier than finding the cause of that issue.

Thanks

Hi @maliming,

sure, this is my LoginModel extension:

  [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(AccountPageModel), typeof(LoginModel))]
    public class CustomLoginModel : LoginModel
    {
        private readonly IDataFilter _dataFilter;
        private readonly ICurrentTenant _currentTenant;
        private readonly IExtendedOrganizationUnitAppService _extendedOrganizationUnitAppService;
        public CustomLoginModel(IAuthenticationSchemeProvider schemeProvider,
            IOptions<AbpAccountOptions> accountOptions,
            IAbpRecaptchaValidatorFactory recaptchaValidatorFactory,
            IAccountExternalProviderAppService accountExternalProviderAppService,
            ICurrentPrincipalAccessor currentPrincipalAccessor,
            IOptions<IdentityOptions> identityOptions,
            IOptionsSnapshot<reCAPTCHAOptions> reCaptchaOptions,
            ICurrentTenant currentTenant,
            IExtendedOrganizationUnitAppService extendedOrganizationUnitAppService,
            IDataFilter dataFilter) : base(schemeProvider, accountOptions, recaptchaValidatorFactory, accountExternalProviderAppService, currentPrincipalAccessor, identityOptions, reCaptchaOptions)
        {
            _dataFilter = dataFilter;
            _currentTenant = currentTenant;
            _extendedOrganizationUnitAppService = extendedOrganizationUnitAppService;
        }

        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 = await SettingProvider.IsTrueAsync(AccountSettingNames.IsSelfRegistrationEnabled);

            Volo.Abp.Identity.IdentityUser user;

            Guid? tenantId;

            // disable TenantFilter
            using (_dataFilter.Disable<IMultiTenant>())
            {
                // read tenant from username (unique for all tenants, by overwritten RegisterModel)
                // if we login with disabled IMultiTenant DataFilter, we have issues with claims
                // therefore we read tenant from username, then switch the current tenant and login

                await ReplaceEmailToUsernameOfInputIfNeeds();
                tenantId = await _extendedOrganizationUnitAppService.GetTenantByUsername(LoginInput.UserNameOrEmailAddress);
            }
            
            // change the current tenant
            using (CurrentTenant.Change(tenantId))
            {

                IsLinkLogin = await VerifyLinkTokenAsync();


                var 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 = base.ReturnUrl,
                        returnUrlHash = base.ReturnUrlHash,
                        rememberMe = LoginInput.RememberMe,
                        linkUserId = base.LinkUserId,
                        linkTenantId = base.LinkTenantId,
                        linkToken = base.LinkToken
                    });
                }

                if (result.IsLockedOut)
                {
                    return RedirectToPage("./LockedOut", new
                    {
                        returnUrl = ReturnUrl,
                        returnUrlHash = ReturnUrlHash
                    });
                }

                if (result.IsNotAllowed)
                {
                    var notAllowedUser = await GetIdentityUser(LoginInput.UserNameOrEmailAddress);
                    if (notAllowedUser.IsActive && await UserManager.CheckPasswordAsync(notAllowedUser, LoginInput.Password))
                    {
                        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();
                }

                user = await GetIdentityUser(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
                    });
                }
            }

            return RedirectSafely(ReturnUrl, ReturnUrlHash);
        }


    }
  • ABP Framework version: v6.0.0
  • UI type: MVC
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no

Hello,

as my users always complained about tenant-specific URLs, I decided to develop a unified login. Therefore, I extended all required methods such as LoginModel, ForgotPasswordModel, ResetPasswordModel and so on to work without multi-tenancy.

For the LoginModel, I disable multi-tenancy filter, get the tenant ID by the inserted user name (which is unique for all tenants), change CurrentTenant to the user's tenant and then perform PasswordSignIn.

Everything works almost as expected, but I got a strange issue: in some very rare cases, when users try to login from the unified login page, they receive an error 500, because a duplicate CmsUser insertion is tried. I can see from the sql server logs that it tries to find the cms user with tenant null:

exec sp_executesql N'SELECT TOP(1) [c].[Id], [c].[ConcurrencyStamp], [c].[Email], [c].[EmailConfirmed], [c].[ExtraProperties], [c].[IsActive], [c].[Name], [c].[PhoneNumber], [c].[PhoneNumberConfirmed], [c].[Surname], [c].[TenantId], [c].[UserName]
FROM [CmsUsers] AS [c]
WHERE ((@__ef_filter__p_0 = CAST(1 AS bit)) OR ([c].[TenantId] = @__ef_filter__CurrentTenantId_1)) AND ([c].[Id] = @__id_0)
ORDER BY [c].[Id]',N'@__ef_filter__p_0 bit,@__ef_filter__CurrentTenantId_1 uniqueidentifier,@__id_0 uniqueidentifier',@__ef_filter__p_0=0,@__ef_filter__CurrentTenantId_1=null,@__id_0='[userid]'

Important part: it searches for __CurrentTenantId_1=null although I changed the tenant.

Of course this doesn't return a user (as the user is tenant user and not host user) and therefore it tries to insert a new CmsUser, (interestingly with the correct switched tenantid!). This if course produces an error because of the insertion of a duplicate Id.

I don't understand why this happens only in rare cases and not always. But I want to figure out and therefore, I need to know where this CmsUser Inserting during login process takes place. I couldn't find it from the sources. Maybe you could point me to the right direction to help me fix it.

Thanks in advance!

https://github.com/abpframework/abp/issues/13999

If somebody else also struggles with this, it seems that it will be fixed in 6.0 final. Thanks.

Btw. I think it's related this issue, therefore it seems to be a new feature, not bad at all, but there should be a possibility to disable it, since we might want the users to set their SocialSecurityNumber, but not their UserLevel, Department, Income,...

Hi @mahmut.gundogdu

I think you misunderstood me.

I'm using MVC and since 6.0.0-rc.3, the users of my application can change the ExtraProperties in their profile settings. I just want to disable this, as I'm using ExtraProperties that should be set only by administrators.

In Module Extensions Documentation there is also a sample where a User Type (Regular/ Moderator/ Superuser) is set by ExtraProperties. Of course we wouldn't want the user to set himself as Superuser ;-) I just implemented a similar logic.

Best regards Claus

  • ABP Framework version: v6.0.0-rc.4
  • UI type: MVC
  • DB provider: EF Core
  • Tiered (MVC) or Identity Server Separated (Angular): no

Hello,

I extended IdentityUser with some properties. Since 6.0.0-rc.3, users can edit these properties themselves by navigating to My Account > Personal Information. While this might be helpful in some situations, in my setup it is critical that the user may NOT edit the properties (e.g. I use them to set special discount conditions).

Is this a bug or was this intentionally added? If the latter, it would have been great if this had been announced before, as this might be a critical change for some users like me. I only discovered it by accident.

We can hide properties by setting: options.UI.OnTable.IsVisible = false; options.UI.OnEditForm.IsVisible = false; options.UI.OnCreateForm.IsVisible = false;

However this hides the properties in administrative User Management UI as well. I want the properties to be set only by administrative users. What can I do? It would be the best if we could set for every property whether the user can edit it himself.

Thanks and best Claus

Hi,

sorry for my late response. Of course I can share the steps to reproduce. It's very easy to reproduce:

  1. create new MVC commercial project (abp 5.3 rc.3) without public website:

  2. add cmskit pro via abp cli:

  3. an empty migration is created:

Best regards Claus

Showing 1 to 10 of 29 entries
Made with ❤️ on ABP v8.3.0-preview Updated on July 05, 2024, 15:13