I have a controller and I would like to require Authorization for all actions by default except a couple. So in the example below all actions should require authentication except the Index. I don't want to decorate every action with the Authorize, I just want to override the default authorization in certain circumstances probably with a custom filter such as NotAuthorize.
[Authorize]
public class HomeController : BaseController
{
[NotAuthorize]
public ActionResult Index()
{
// This one wont
return View();
}
public ActionResult About()
{
// This action will require authorization
return View();
}
}
Ok, this is what I did. If there is a better way let me know.
public class NotAuthorizeAttribute : FilterAttribute
{
// Does nothing, just used for decoration
}
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
// Check if this action has NotAuthorizeAttribute
object[] attributes = filterContext.ActionDescriptor.GetCustomAttributes(true);
if (attributes.Any(a => a is NotAuthorizeAttribute)) return;
// Must login
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
filterContext.Result = new HttpUnauthorizedResult();
}
}
}
What about [AllowAnonymous] ??
MVC4 has a new attribute exactly meant for this [AllowAnonymous] (as pointed out by Enrico)
[AllowAnonymous]
public ActionResult Register()
Read all about it here:
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
Here's what I would do, similar to Craig's answer with a couple of changes:
1) Create an ordinary attribute deriving from System.Attribute (no need to derive from FilterAttribute since you aren't going to be using anything FilterAttribute provides).
Maybe create a class hierarchy of attributes so you can test based on the hierarchy, e.g.
Attribute
AuthorizationAttribute
AuthorizationNotRequiredAttribute
AuthorizationAdminUserRequiredAttribute
AuthorizationSuperUserRequiredAttribute
2) In your BaseController override the OnAuthorization method rather than the OnActionExecuting method:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
var authorizationAttributes = filterContext.ActionDescriptor.GetCustomAttributes(true).OfType<AuthorizationAttribute>();
bool accountRequired = !authorizationAttributes.Any(aa => aa is AuthorizationNotRequiredAttribute);
I like the approach of being secure by default: even if you forget to put an attribute on the Action it will at least require a user to be logged in.
Use a custom filter as described in Securing your ASP.NET MVC 3 Application.
Mark the controller with [Authorize]
[Authorize]
public class YourController : ApiController
Mark actions you want public with :
[AllowAnonymous]
Little late to the party, but I ended up creating a Controller-level auth attribute and an Action-level auth attribute and just skipping over the Controller auth if the Action had its own Auth attribute. See code here:
https://gist.github.com/948822
Related
I have a custom AuthorizeAttribute written in MVC. I have it applied to a controller for security. In that AuthorizeAttribute class I have written are several variables I gathered from a web service call I would like to access inside the controller to prevent having to call the web service again. Is this possible?
Your best approach would be to use HttpContext.Current.Items for storing those variables because that data will only be valid for a single http request. Something like this:
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
if (httpContext.User.Identity == null) return false;
if (!httpContext.Request.IsAuthenticated) return false;
var user = new WSUser(); //get this from your webservice
if(user == null) return false;
httpContext.Items.Add("prop", user.Property);
return user.Authorized;
}
}
public class HomeController : Controller
{
[CustomAuthorize]
public ActionResult Index()
{
var property = (string) HttpContext.Items["prop"];
return View();
}
}
You would also want to encapsulate logic for storing and retrieving items from HttpContext.Current into a separate class to keep the code clean and to follow Single responsibility principle
You could save these variables in a static class to store it. But, a elegant solution would be to have a modelbinder object that you call like parameter in your controller and that read the static class and return the properties that you need.
Perhaps, if you are applying security, the best will be that call the webservices each once.
Reference for your custom model binder
I am trying to remove the "X-Frame-Options" header for only a specific controller's actions using:
protected override void OnResultExecuting(ResultExecutingContext filterContext)
{
filterContext.HttpContext.Response.Headers.Remove("X-Frame-Options");
base.OnResultExecuting(filterContext);
}
However, that doesn't seem to work at all. The only way I can get it to work at all on my site is to add this code to the global.asax below. I am pretty sure I am missing the correct step in the ASP.NET MVC / IIS pipeline that allows me to overwrite the IIS setting of that header. Is this possible?
protected void Application_EndRequest()
{
Response.Headers.Remove("X-Frame-Options");
}
As for why I want to do this, I am building a widget that user's will be able to use on their personal sites through the use of an iframe, but allow them to post back information to our site. I realize there are security implications to turning this header off, and while I welcome any suggestions on how to mitigate those risks, I just want to know if what I am asking is possible.
OnResultExecuting happens too early in the MVC lifecycle. The header has not been set yet.
What you need is the OnResultExecuted method which is run after the View is rendered.
Here's how you write a filter class for what you are looking for:
using System.Web.Mvc;
namespace Test.Filters
{
public class RemoveXFrameOptionsAttribute : ActionFilterAttribute
{
public override void OnResultExecuted(ResultExecutedContext filterContext)
{
filterContext.HttpContext.Response.Headers.Remove("X-Frame-Options");
base.OnResultExecuted(filterContext);
}
}
}
Then to use it, decorate whatever Controller or Action you want this filter applied.
[RemoveXFrameOptions]
public class TestController : Controller
{
public ActionResult Index()
{
return View();
}
}
or
public class TestController : Controller
{
[RemoveXFrameOptions]
public ActionResult Index()
{
return View();
}
}
I have a question about the [Authorization] annotation. I have a controller with the [Authorization(Roles ="admin") annotation. In the controller I have one action that doesn't need to be admin, but still logged in. How is this done? I have tried creating a custom AuthorizationAttribute class, and placing it on the action. This doesn't work, is there a way to override the [Authorization] annotation?
Cheers.
[Authorize(Roles = "admin")]
public class UserController : Controller
{
[OwnDataAuthorize]
public async Task<ActionResult> Details(string id)
{
//.....
}
//...
}
I found the answer to my question here. I was doing it correctly but because of a bug with mvc 5 I had to implement another interface as well in my custom class:
public class OwnDataAuthorize : AuthorizeAttribute, IOverrideFilter {
//....
/// <summary>
/// Only implemented because of a bug in MVC 5.
/// </summary>
public System.Type FiltersToOverride
{
get { return typeof (IAuthenticationFilter); }
}
}
There isn't a simple way to do this, except for moving the Authorize attribute into every controller method/action. So you would need to remove the [Authorize(Roles = "admin")] and add it to every method except for the one you want to authorize.
A work around would be to use the [AllowAnonymous] attribute with an if statement inside the controller action to check for IsAuthenticated:
[AllowAnonymous]
public async Task<ActionResult> Details(string id)
{
if(!User.Identity.IsAuthenticated)
//redirect to loging
//other logic here
...
}
I have an ASP.NET MVC based application that allows different levels of access depending on the user. The way it currently works is when a user accesses a page, a check is done against the database to determine the rights that user has. The view is then selected based on the level of access that user has. Some users see more data and have more functionality available to them than do others. Each page also makes a variety of ajax calls to display and update the data displayed on the page.
My question is what is the best way to ensure that a particular ajax call originated from the view and was not crafted manually to return or update data the user does not have access to? I would prefer not to have to go to the database to re-check every time an ajax call is made since that was already done when the user initially loaded the page.
Check out the Authorize Attribute, you can put it on an entire controller or just specific methods within your controller.
Examples:
[Authorize(Roles = "Administrator")]
public class AdminController : Controller
{
//your code here
}
or
public class AdminController : Controller
{
//Available to everyone
public ActionResult Index()
{
return View();
}
//Just available to users in the Administrator role.
[Authorize(Roles = "Administrator")]
public ActionResult AdminOnlyIndex()
{
return View();
}
}
Alternately, you can write a custom Authorize attribute to provide your own logic.
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
IPrincipal user = httpContext.User;
var validRoles = Roles.Split(',');//Roles will be a parameter when you use the Attribute
List<String> userRoles = GetRolesFromDb(user);//This will be a call to your database to get the roles the user is in.
return validRoles.Intersect(userRoles).Any();
}
}
To use:
[CustomAuthorizeAttribute(Roles = "Admin,Superuser")]
public class AdminController : Controller {
}
If iyou are using a post use
[Authorize]
[ValidateAntiForgeryToken]
If iyou are using a get use
[Authorize]
You can also use this custom attribute
public class HttpAjaxRequestAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, System.Reflection.MethodInfo methodInfo)
{
if (!controllerContext.HttpContext.Request.IsAjaxRequest())
{
throw new Exception("This action " + methodInfo.Name + " can only be called via an Ajax request");
}
return true;
}
}
Then decorate your action as below
[Authorize]
[HttpAjaxRequest]
public ActionResult FillCity(int State)
{
//code here
}
Remember to "Mark/Tick" if this solve your problem.
It depends on what type of session mechanisam you are using . Are you using default membership provider ? If not than you can pass user's id and sessionid make sure that user session is valid and user has required permission to make that call .
Along with the Authorize attribute, you can also allow only Ajax requests using custom attributes as shown here.
Thanks
I need to have some navigation options, that require keys that are specific to the current user, that reside in a masterpage. I need some advice on best practise.
In have the following links in a left nav in a masterpage
http://www.example.com/manageShop/123
http://www.example.com/addProductToShop/123
http://www.example.com/addStaffToShop/123
Where '123' is the shop id that the current user is the manager of. I need some way of passing this to the masterpage
Currently I'm going something to this effect:
<li><%= Html.ActionLink<ShopController>(x => x.ManageShop((int)Session["ShopKey"]), "Manage")%></li>
I thought this was a good idea as I only have to set the ShopKey once in the session and its done, the down side is that iv noticed that the session gets mixed if you have the site open is two tabs.
Alternatively I tried this:
<li><%= Html.ActionLink<ShopController>(x => x.ManageShop((int)ViewData["ShopKey"]), "Manage")%></li>
But this means you have to keep setting the ViewData in every action in every controller. Which is awful.
EDIT: I have had alook at filters like eu-ge-ne suggested below, but I dont this really solves my problem as I still have the issue of setting the ShopKey everywhere?
What is the solution?
You can create custom filter for this:
public class UserKeyAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["UserKey"] = UserKey;
}
}
and use it on your controller or controller actions
[UserKey]
public class YourController : Controller
{
// or
public class YourController : Controller
{
[UserKey]
public ActionResult Index()
{
or use Controller.OnActionExecuting() (or even create base controller for this as Arnis L. said):
public class BaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["UserKey"] = UserKey;
}
}
// and then derive your controllers from BaseController
public class YourController : BaseController
{