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:
User A logs in with user/password ->OK (SimpleMembershipProvider)
Authenticated User A selects Hotel 1 -> User A is an "Administrator" for Hotel 1.
Authenticated User A change to Hotel 2 -> User A is a "User" in Hotel 2
I can have any number of hotels.
User A -> Hotel 1 -> { "Administrator", "User"}
User A -> Hotel 2 -> { "User" }
User A -> Hotel 3 -> { "Owner" }
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!
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.
Related
I'm working on a ASP.Net MVC 5 app and using ASP.Net identity 2, and need to authorize users based on roles and permissions. roles and permissions is not related to each other. for example, to access "action1" action method,( "admin" role ) or ( combination of "role1" and "permission1" ) must exist for him, but other users that is not in "admin" role or combination of ( "role1" and "permission1") is not true for theirs, don't allow to access that action method.
how i can do this scenario?
do claims based authorization useful in this manner?
or i must implement Permission entity and custom AuthorizeAttribute? if true how?
best regards
Check out the ResourceAuthorize attribute in the Thinktecture.IdentityModel.Owin.ResourceAuthorization.Mvc package.
This attribute authorizes a user based on an action (e.g. read) and a resource (e.g. contact details). You can then base whether or not they are allowed to perform that action on a resource based on a claim (e.g. their presence in a role).
See here for a good example.
Might not be exactly what you are looking for, but you can take inspiration and implement your own authorization attribute using similar logic.
This is custom made Authorize which checks permission from database.
For example you have 3 bools for permission Account,Clients,Configuration
and you want to restrict user based on them.
you can add even two permission on one action, for example you have a method which can be accessed by Account and Client permission than you can add following line
Modify this to use roles with permissions in this, this is the easiest and best way to handle it.
[PermissionBasedAuthorize("Client, Account")]
This method below is which check the bools from database.
public class PermissionBasedAuthorize : AuthorizeAttribute
{
private List<string> screen { get; set; }
public PermissionBasedAuthorize(string ScreenNames)
{
if (!string.IsNullOrEmpty(ScreenNames))
screen = ScreenNames.Split(',').ToList();
}
public override void OnAuthorization(HttpActionContext actionContext)
{
base.OnAuthorization(actionContext);
var UserId = HttpContext.Current.User.Identity.GetUserId();
ApplicationContext db = new ApplicationContext();
var Permissions = db.Permissions.Find(UserId);
if (screen == null || screen.Count() == 0)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
bool IsAllowed = false;
foreach (var item in screen)
foreach (var property in Permissions.GetType().GetProperties())
{
if (property.Name.ToLower().Equals(item.ToLower()))
{
bool Value = (bool)property.GetValue(Permissions, null);
if (Value)
{
IsAllowed = true;
}
break;
}
}
if (!IsAllowed)
{
actionContext.Response = new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized);
}
}
}
I implemented a Permission-based extension for Microsoft Identity 2 membership system. But in this extension, permissions and roles are related together. there is a many-to-many relation between them. Also you can have a complex authentication with combination of roles and permissions. I suppose it can help you to do permission based authentication.
You can do permission authentication in two ways:
First approach:
// GET: /Manage/Index
[AuthorizePermission(Name = "Show_Management", Description = "Show the Management Page.")]
public async Task<ActionResult> Index(ManageMessageId? message)
{
//...
}
Second approach:
// GET: /Manage/Users
public async Task<ActionResult> Users()
{
if (await HttpContext.AuthorizePermission(name: "AllUsers_Management", description: "Edit all of the users information."))
{
return View(db.GetAllUsers());
}
else if (await HttpContext.AuthorizePermission(name: "UnConfirmedUsers_Management", description: "Edit unconfirmed users information."))
{
return View(db.GetUnConfirmedUsers());
}
else
{
return View(new List<User>());
}
}
Also it's an open source and free extension and you can access to the repository here.
I have an authorization requirement to have my security roles based on actions methods, which can not be achieved using the default asp.net mvc authorization. so i have created the following action filter, to implement my custom authorization requirments:-
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false, Inherited = true)]
public class CheckUserPermissionsAttribute : ActionFilterAttribute
{
Repository repository = new Repository();
public string Model { get; set; }
public string Action { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
// var user = User.Identity.Name; // or get from DB
string ADusername = filterContext.HttpContext.User.Identity.Name.Substring(filterContext.HttpContext.User.Identity.Name.IndexOf("\\") + 1);
if (!repository.can(ADusername,Model,Action)) // implement this method based on your tables and logic
{
filterContext.Result = new HttpUnauthorizedResult("You cannot access this page");
}
base.OnActionExecuting(filterContext);
}
}
which is calling the following Repository method:-
public bool can(string user, string Model, string Action)
{
bool result;
bool result2;
int size = tms.PermisionLevels.Where(a5 => a5.Name == Action).SingleOrDefault().PermisionSize;
var securityrole = tms.SecurityroleTypePermisions.Where(a => a.PermisionLevel.PermisionSize >= size && a.TechnologyType.Name == Model).Select(a => a.SecurityRole).Include(w=>w.Groups).Include(w2=>w2.SecurityRoleUsers).ToList();//.Any(a=> a.SecurityRoleUsers.Where(a2=>a2.UserName.ToLower() == user.ToLower()));
foreach (var item in securityrole)
{
result = item.SecurityRoleUsers.Any(a => a.UserName.ToLower() == user.ToLower());
var no = item.Groups.Select(a=>a.TMSUserGroups.Where(a2=>a2.UserName.ToLower() == user.ToLower()));
result2 = no.Count() == 1;
if (result || result2)
{
return true;
}}
return false;
i am calling the action filter inside my controller class as follow:-
[CheckUserPermissions(Action = "Read", Model = "Server")]
but i have the following concerns:-
inside my repository i will be retrieving all the users and groups (when calling the .Tolist()), and then check if the current login user is inside these values. which will not be very extensible when dealing with huge number of users?
each time the user call an action method the same security code will run (of course ideally the user permission might chnage during the user session),,, which might cause performance problems ?
So can anyone adice how i can improve my current implementation, taking the two concerns in mind ?
Thanks
I would change your approach and use claims based authentication.
This way you have a lot more granular control over authorization (it can be driven by resource and actions).
You can use a ClaimsAuthorizationManager to check access at every level in a central place.
This article expains how to implement this and also use secure session tickets to save accessing the database everytime.
http://dotnetcodr.com/2013/02/25/claims-based-authentication-in-mvc4-with-net4-5-c-part-1-claims-transformation/
I'm very new to MVC. I'm doing a Custom Membership asp.net MVC4. Above are the Table & data i have created. I try to override isinrole method but its not working.Below is the sample code & i don't know where to edit it.
public override bool IsUserInRole(string userName, string roleName)
{
User user = repository.GetUser(userName);
Role role = repository.GetRole(roleName);
if (!repository.UserExists(user))
return false;
if (!repository.RoleExists(role))
return false;
return user.Name == role.Name;
}
Now i need to get list of roles for a user from UserInRole Table but the this table is not visible.I have to check whether the User role are matching with User.IsInRole("Administrator") .Hope anybody can guide me.Thank u in advance.
It's not working because you're comparing the role name with the user name ("a" != "Administrator"). You need something more like this.
public override bool IsUserInRole(string userName, string roleName)
{
User user = repository.GetUser(userName);
Role role = repository.GetRole(roleName);
if (!repository.UserExists(user))
return false;
if (!repository.RoleExists(role))
return false;
return user.Role.ID == role.ID;
}
It may differ depending on how your entities are setup though.
In my web application registered users can add new content and edit it later. I want only the content's author to be able to edit it. Is there any smart way of doing this other than manually writing code in all the action methods that checks if the logged user is the same as the author? Any attribute that I could use for the whole controller?
Any attribute that I could use for the whole controller?
Yes, you could extend the Authorize attribute with a custom one:
public class AuthorizeAuthorAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
var isAuthorized = base.AuthorizeCore(httpContext);
if (!isAuthorized)
{
// the user is either not authenticated or
// not in roles => no need to continue any further
return false;
}
// get the currently logged on user
var username = httpContext.User.Identity.Name;
// get the id of the article that he is trying to manipulate
// from the route data (this assumes that the id is passed as a route
// data parameter: /foo/edit/123). If this is not the case and you
// are using query string parameters you could fetch the id using the Request
var id = httpContext.Request.RequestContext.RouteData.Values["id"] as string;
// Now that we have the current user and the id of the article he
// is trying to manipualte all that's left is go ahead and look in
// our database to see if this user is the owner of the article
return IsUserOwnerOfArticle(username, id);
}
private bool IsUserOwnerOfArticle(string username, string articleId)
{
throw new NotImplementedException();
}
}
and then:
[HttpPost]
[AuthorizeAuthor]
public ActionResult Edit(int id)
{
... perform the edit
}
I would:
Save the db.aspnet_Users columm UserId (Guid) against the content record
Write an extension method for your content model which verifies the current users Guid against the saved contents User Guid
I would write some code that overrides this functionality for your Admin logins (I would create an Admin Role).
Greetings,
can someone give me some advices or links that will help me to implement to following scenario.
Page will be written in asp.net mvc. Authorization is going to be implemented by Memberships. The scenario is as follows:
User1 has just logged in. After a
while, User2 attempts to login with
success. Then user1 should be notified
that User2 has just logged in. Additionally User2
should be notified that User1 is
online.
How can I achieve something like that? It should also be possible for these users to write messages to each other. (chat like).
There are methods to do this in the asp.net membership providers, specifically, IsUserOnline() and something like CountUsersOnline(). The only problem with these methods is that they are really lame. They depend on the membership provider's LastActivityDate() and a window you can set in web.config. In other words, the user is considered online if his last encounter with the membership provider plus the time window in web.config has not expired.
We took this scenairo and made it work for us by setting up a Comet server, and pinging the Web server every ten minutes. When the Web server is pinged, it updates the LastActivityDate of the membership provider.
We set the activity window to 12 minutes, as well as the Session timer. This allows us to determine who is online to an accuracy of aproximately ten minutes.
Here is the line in Web.config:
<membership userIsOnlineTimeWindow="12">
Here is jQuery Comet server:
function getData() {
$.getJSON("/Account/Timer", gotData);
}
// Whenever a query stops, start a new one.
$(document).ajaxStop(getData, 600000);
// Start the first query.
getData();
Here's our server code:
public JsonResult Timer()
{
MembershipUser user = Membership.GetUser(User.Name);
user.LastActivityDate = DateTime.Now;
Membership.UpdateUser(user);
// You can return anything to reset the timer.
return Json(new { Timer = "reset" }, JsonRequestBehavior.AllowGet);
}
Sounds to me like you need some jQuery polling to happen.
You can easily do a jQuery post to an ActionResult which would then check for users online and returns a PartialView back to the calling jQuery function.
The returning PartialView might have all the users logged in which can then be popped up in some sort of animating panel.
Use javascript to execute a timed poll back to the controller.
You cannot get onlines live in web. you should refresh page or refresh content with ajax -or else-. So it gonna solve i think.
ps. chat and online issues, you have two options; you can store them in database -its what i suggest- or store in memory, you may want to look this
Tables:
Users
-Id / int - identity
-LoginTime / datetime
-IsOnline / bit
Friends
-Id / int - identity
-FirstUserId / int
-SecondUserId / int
public class UserInformation
{
public IList<User> OnlineFriends { get; set;}
public IList<User> JustLoggedFriends { get; set; } /* For notifications */
}
public class UserRepository
{
public UserInformation GetInformation(int id, DateTime lastCheck)
{
return Session.Linq<User>()
.Where(u => u.Id == id)
.Select(u => new {
User = u,
Friends = u.Friends.Where(f => f.FirstUser.Id == u.Id || f.SecondUser.Id == u.Id)
})
.Select(a => new UserInformation {
JustLoggedFriends = u.Friends.Where(f => f.IsOnline && f.OnlineTime >= lastCheck).ToList(),
OnlineFriends = u.Friends.Where(f => f.IsOnline).ToList()
})
.ToList();
}
}
public class UserService
{
public UserInformation GetInformation(int id, DateTime lastCheck)
{
return repository.GetInformation(id, lastCheck);
}
}
UI:
public class UserController
{
public ActionResult Index()
{
var userId = (int)Session["UserId"];
var lastCheck = (DateTime)Session["LastCheck"];
UserInformation info = userService.GetInformation(userId, lastCheck);
Session["LastCheck"] = DateTime.Now;
//show up notifications and online users.
}
public ActionResult Login()
{
User user = null; // TODO: get user by username and password
Session["UserId"] = user.Id;
Session["LastCheck"] = DateTime.Now;
}
}