R – Handle security denied in ASP.NET MVC with AspNetSqlRoleProvider

asp.net-mvcSecurity

I'm looking to secure different areas of my MVC application to prevent standard user's from accessing admin type views. Currently, if any user is logged in and they attempt to view the About page (out of the box template in visual studio), it will simply redirect them to the login page. I'd prefer the user is informed that they do not have permission to view the page.

[Authorize(Roles="Admin")]
public ActionResult About()
{
    return View();
}

It seems redundant to send an already authenticated user to the login page when they don't have permission.

Best Answer

Here is an attribute that I've created that can be used to direct to an unauthorized security action. it also allows you to specify a Reason which will be passed to the Unauthorized action on the Security controller, which you can then use for the view.

You can create any number of properties to customize this to fit your particular application, just make sure to add it to the RouteValueDictionary.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)]
public sealed class ApplySecurityAttribute : ActionFilterAttribute
{
    private readonly Permission _permission;

    public ApplySecurityAttribute(Permission permission)
        : this(permission, string.Empty) {}

    public ApplySecurityAttribute(Permission permission, string reason)
    {
        _permission = permission
        Reason = reason;
    }

    public string Reason { get; set; }

    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (!PermissionsManager.HasPermission(_permission)) // Put security check here
        {
            var routeValueDictionary = new RouteValueDictionary
                                       {
                                           { "controller", "Security" }, // Security Controller
                                           { "action", "Unauthorized" }, // Unauthorized Action
                                           { "reason", Reason }          // Put the reason here
                                       };

            filterContext.Result = new RedirectToRouteResult(routeValueDictionary);
        }

        base.OnActionExecuting(filterContext);
    }
}

Here is the security controller

public class SecurityController : Controller
{
    public ViewResult Unauthorized(string reason)
    {
        var vm = new UnauthorizedViewModel { Reason = reason };

        return View(vm);
    }
}

Here is the attribute declaration on a controller you wish to secure

[ApplySecurity(Permission.CanNuke, Reason = "You are not authorized to nuke!")]

Here is how PermissionsManager does the check to see if the user has the permissions

public static class PermissionsManager
{
    public static bool HasPermission(EZTracPermission permission)
    {
        return HttpContext.Current.GetCurrentUser().Can(permission);
    }
}