Question

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!

Was it helpful?

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top