views:

781

answers:

3

Hi folks,

I wish to lock out access to a user's EDIT page (eg. /user/pure.krome/edit) if

a) Identity.IsAuthenticated = false

or they are authenticated but

b) Idenitity.Name != user name of the user page they are trying to edit
c) Identity.UserType() != UserType.Administrator // This is like a Role, without using RoleProviders.

I'm assuming u can decorate a controller or a controller's action method with something(s), but i'm just not sure what?

+1  A: 

A custom attribute derived from AuthorizeAttribute is what I use to do this. Override the OnAuthorize method and implement your own logic.

public class OnlyUserAuthorizedAttribute : AuthorizeAttribute
{
    public override void OnAuthorize( AuthorizationContext filterContext )
    {
        if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
        {
            filterContext.Result = new HttpUnauthorizeResult();
        }
        ...
    }
}
tvanfosson
Interesting ... but ... I know I need to substitute my own logic, over yours. But your code only answer's part (a). I can also check for (c). But I have no idea how the code could know what to check for (b). I have the Identity.Name ... but how does this attribute - at that point of time - know what the view data is? or any data in the action method (which i'm assuming hasn't been run, yet .. because attributes get handled first.
Pure.Krome
The AuthorizationContext parameter includes references to the Controller, HttpContext, ResultContext, RouteData, and Result. You should be able to get all you need from the RouteData. To generalize, add a property to your attribute that allows you to specify the route data key to use for the user's name. You'll have to do the DB stuff to check the user's type independently. You might want to have two constructors: one that takes your database context/layer interface and another that takes no parameters and creates the default database context/layer. Use the former in unit testing.
tvanfosson
A: 

I implemented the following ActionFilterAttribute and it works to handle both authentication and roles. I am storing roles in my own DB tables like this:

  • User
  • UserRole (contains UserID and RoleID foreign keys)
  • Role

    public class CheckRoleAttribute : ActionFilterAttribute { public string[] AllowedRoles { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string userName = filterContext.HttpContext.User.Identity.Name;
    
    
    
    if (filterContext.HttpContext.User.Identity.IsAuthenticated)
    {
        if (AllowedRoles.Count() > 0)
        {
            IUserRepository userRepository = new UserRepository();
            User user = userRepository.GetUser(userName);
            bool userAuthorized = false;
            foreach (Role userRole in user.Roles)
            {
                userAuthorized = false;
                foreach (string allowedRole in AllowedRoles)
                {
                    if (userRole.Name == allowedRole)
                    {
                        userAuthorized = true;
                        break;
                    }
                }
            }
            if (userAuthorized == false)
            {
                filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
            }
        }
        else
        {
            filterContext.HttpContext.Response.Redirect("/Account/AccessViolation", true);
        }
    }
    else
    {
        filterContext.HttpContext.Response.Redirect(FormsAuthentication.LoginUrl + String.Format("?ReturnUrl={0}", filterContext.HttpContext.Request.Url.AbsolutePath), true);
    }
    
    }

I call this like this...

    [CheckRole(AllowedRoles = new string[] { "admin" })]
    public ActionResult Delete(int id)
    {
        //delete logic here
    }
mikerennick
In my first line above I meant to say "*authentication* and roles"
mikerennick
Mikerennick, do you do any caching of users or roles?
Pure.Krome
I am not using caching -- but I see no reason why the list of roles assigned to a user could not be cached. You could update the cache when changes are made to assigned roles. The above code has no bearing on caching of users, as I am merely looking at httpcontext for the current user.I should also mention that adding a check for allowed users is the same in principle as the check for allowed roles.
mikerennick