Search code examples
asp.net-mvcroleproviderrole-base-authorization

MVC 4 - RoleProvider to manage authenticated users permissions with different scopes


Here is my problem with my MVC 4 Internet project using Forms Authentication. Lets say i have hotels and i want the authorized users accessing each under different roles.

So the user logs in. Then from a dropdownlist selects the target Hotel and the application´s security responds accordingly.

I would need something like [Authorize(Roles = "Administrator")] but only in that hotel scope.

My first aproach was inheriting from AuthorizeAttribute and override AuthorizeCore like is shown in this thread

From there I could get the HttpContext.Session["HotelId"] and query a UserRolesInHotel table. That said, I should have my own roles table with a structure similiar to UserId, RoleId, HotelId. So SimpleRolePrivider comes short to this task and i would be forced to create a CustomeRoleProvider. RoleProvider Methods don´t handle extra params as I need like HotelId when adding a new role to a user.

For clarification:

  1. User A logs in with user/password ->OK (SimpleMembershipProvider)
  2. Authenticated User A selects Hotel 1 -> User A is an "Administrator" for Hotel 1.
  3. Authenticated User A change to Hotel 2 -> User A is a "User" in Hotel 2

I can have any number of hotels.

  1. User A -> Hotel 1 -> { "Administrator", "User"}
  2. User A -> Hotel 2 -> { "User" }
  3. User A -> Hotel 3 -> { "Owner" }
  4. User A -> Hotel 4 -> { "Administrator" }

The list of roles is always the same.

I´ve been struggling with this implementation for a couple of days and i couldn´t come up with a pratical solution. Any thougths would be much appreciated.

Thanks!


Solution

  • This is what I did:

    • Added a DefaultBuildingId to the user profile.
    • Then I created a CustomRoleProvider and overrided GetRolesForUser method like this

      public override string[] GetRolesForUser(string userName)
      {
          if (HttpContext.Current.Session != null)
          {
              var user = _userRepository.GetByName(userName);
      
              if (!user.IsActive)
              {
                  throw new ApplicationException(string.Format("some message {0}", userName));
              }
      
              if (HttpContext.Current.Session["BuildingId"] == null)
              {
                  var building = _buildingRepository.Get(user.DefaultBuildingId);
                  if (building == null)
                  {
                      throw new ApplicationException("error message");
                  }
      
                  HttpContext.Current.Session["BuildingId"] = building.BuildingId;
              }
      
              int buildingId = Convert.ToInt32(HttpContext.Current.Session["BuildingId"]);
              return _userRepository.GetRolesForUserInBuilding(user.UserId, buildingId).ToArray();
          }
      
          throw new ApplicationException("error message.");
      }
      
      • Added a custom AuthorizeAttribute

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
        var authorized = base.AuthorizeCore(httpContext);
        if (!authorized)
        {
            return false;
        }
        
        var repo = UnityManager.Resolve<IUserRepository>();
        var buildingId = (int)httpContext.Session["BuildingId"];
        var userName = httpContext.User.Identity.Name;
        var user = repo.GetByName(userName);
        var userRolesInBuilding = repo.GetRolesForUserInBuilding(user.UserId, buildingId);
        
        foreach (var role in Roles.Split(','))
        {
            if (userRolesInBuilding.Contains(role.Trim()))
            {
                return true;
            }
        }
        
        return false;
        

        }

      • And finally this is how to use it at controller or action level.

        [BuildingAthorize(Roles = "Administrators")]

    I also added a ddl to the layout to let the user change the building and set the new BuildingId overriding the value at the session/db. This way a user can work in different Hotels during the same session and only access areas and functionality he has for that particular hotel.