Page 1 of 1

Multiple Areas - Change url for specific roles

PostPosted: Tue Sep 18, 2018 3:36 pm
by sbenfares
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.

Re: Multiple Areas - Change url for specific roles

PostPosted: Wed Sep 19, 2018 2:26 am
by maliming
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?

Re: Multiple Areas - Change url for specific roles

PostPosted: Wed Sep 19, 2018 7:29 am
by sbenfares
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.

Code: Select allpublic 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 :

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


by

Code: Select all 
//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 :

Code: Select allvar homeUrl = HomeUrlForUser();

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



Then i created a HomeUrlForUser method :

Code: Select allprivate 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 :

Code: Select all
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 :

Code: Select all 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.

Re: Multiple Areas - Change url for specific roles

PostPosted: Wed Sep 19, 2018 11:30 am
by ryancyq
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?
Code: Select all//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
Code: Select allpublic async Task MyMethod(){
    await userManager.Find...Async();
}

Re: Multiple Areas - Change url for specific roles

PostPosted: Wed Sep 19, 2018 1:04 pm
by sbenfares
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:

Re: Multiple Areas - Change url for specific roles

PostPosted: Wed Sep 19, 2018 1:30 pm
by ryancyq
Great to hear that your problems are solved :)