Base solution for your next web application
Open Closed

Multiple Areas - Change url for specific roles #5607


User avatar
0
sbenfares created

Hello,

I’m trying to add 2 areas to my project, i would like to properly redirect users on login. Each user have a specific role, depending on his role he will be redirected to the good area.

What is your advice to handle this properly ?

I’m modifying AccountController and HomeController with a GetAppHomeUrl method which use a User.IsInRole verification.

My problem is with the NormalizeReturnUrl function and the UserManager.UserIsInRole verification which is not working. The IsInRole always return false for my users, but return true for the default admin user.

My users are created in the SeedHelper and roles are set with the context : __context.UserRoles.Add(new UserRole(tenantId, user.Id, role.Id));

When i create the users in SeedHelper, should i use UserManager.AddToRoleAsync to make IsInRoleAsync work ? If yes, should i inject UserManager in the SeedHelper ?

Thanks

PS : Multytenancy disabled.


5 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team

    Your idea is correct. Determine the user role to jump to a different Url. If the data is successfully seeded into the database,

    UserManager.IsInRoleAsync should be no problem. Can you share some code to see?

  • User Avatar
    0
    sbenfares created

    OK so there was 2 problems here :

    1 - The _userManager.IsInRole was not working when i created a user in SeedHelper. To correct this i injected UserManager in SeedHelper and pass it to TenantRoleAndUserBuilder.

    public static class SeedHelper
        {
           private static UserManager UserManager { get; set; }
    
            public static void SeedHostDb(IIocResolver iocResolver)
            {
                UserManager = iocResolver.Resolve<UserManager>();
                WithDbContext<PlatformDbContext>(iocResolver, SeedHostDb);
            }
    
            public static void SeedHostDb(PlatformDbContext context)
            {
                context.SuppressAutoSetTenantId = true;
    
                //Host seed
                new InitialHostDbBuilder(context).Create();
    
                //Default tenant seed (in host database).
                new DefaultTenantBuilder(context).Create();
                new TenantRoleAndUserBuilder(context, 1, UserManager).Create();
           }
    
    ....
    
    }
    

    Then in TenantRoleAndUserBuilder i've added a UserManager readonly field and get it from the constructor. Then in the CreateUser method, i replaced :

    //Assign Admin role to admin user
                    _context.UserRoles.Add(new UserRole(_tenantId, adminUser.Id, adminRole.Id));
                    _context.SaveChanges();
    

    by

    //Assign role to user
                _userManager.AddToRoleAsync(user, role.Name).Wait();
    

    Then the _userManager.IsInRole is working now in the AccountController for my users created in SeedHelper.

    2 - The second problem was to handle properly the url changes depending on the roles of users.

    In the home controller in the Index method i've done this :

    var homeUrl = HomeUrlForUser();
    
                return AbpSession.UserId.HasValue ? 
                    RedirectToAction("Index", "Home", new { area = homeUrl }) : 
                    RedirectToAction("Login", "Account");
    

    Then i created a HomeUrlForUser method :

    private string HomeUrlForUser()
            {
                if (User.IsInRole(StaticRoleNames.Tenants.Candidat)) return "Candidat";
                return User.IsInRole(StaticRoleNames.Tenants.Employeur) ? "Employeur" : "Admin";
            }
    

    In the account controller i've created a method :

    private string CheckReturnUrlForRole(string returnUrl, string userNameOrEmailAddress)
            {
                var user = _userManager.FindByNameOrEmailAsync(userNameOrEmailAddress).Result;
                var isEmployeur = _userManager.IsInRoleAsync(user, StaticRoleNames.Tenants.Employeur).Result;
                var isCandidat = _userManager.IsInRoleAsync(user, StaticRoleNames.Tenants.Candidat).Result;
    
                if (isEmployeur) return returnUrl.Replace("Admin", "Employeur");
                return isCandidat ? returnUrl.Replace("Admin", "Candidat") : returnUrl;
            }
    

    And then in the login method add a call to this CheckReturnUrlForRole function just after GetLoginResultAsync call :

    var loginResult = await GetLoginResultAsync(loginModel.UsernameOrEmailAddress, loginModel.Password, GetTenancyNameOrNull());
    returnUrl = CheckReturnUrlForRole(returnUrl, loginModel.UsernameOrEmailAddress);
    

    Don't know if this is optimal but it works, and i hope it will help someone having same needs.

  • User Avatar
    0
    ryancyq created
    Support Team

    UserManager should not be required to add role to users during seeding.

    Do the user roles get created correctly in the database if you have the following instead of using UserManager?

    //Assign Admin role to admin user
    _context.UserRoles.Add(new UserRole(_tenantId, adminUser.Id, adminRole.Id));
     _context.SaveChanges();
    

    Remember to check the Tenant Id set in the user roles table.

    Also, noticed that you were using userManager.Find...Async().Result which is not recommended.

    You should write similar like this instead

    public async Task MyMethod(){
        await userManager.Find...Async();
    }
    
  • User Avatar
    0
    sbenfares created

    You are right it's working without UserManager, it was a missing tenantId that caused my problem.

    :D Thanks !

    PS : And yes i learned why we should replace XXXX.Result by await thanks to ([https://stackoverflow.com/questions/24623120/await-on-a-completed-task-same-as-task-result])) :ugeek:

  • User Avatar
    0
    ryancyq created
    Support Team

    Great to hear that your problems are solved :)