Currently I have [Authorize]
attributes on all of the methods on my AdminController
except for the Logon
action.
What's the cleanest way to invert this, so I don't have to remember to add the attributes to all methods, but rather add an attribute only to the method(s) that should be available without being logged in?
Would I be better just moving the Logon
action to its own controller, and applying the [Authorize]
attribute to the AdminController class?
In ASP.NET MVC 3 you could implement a custom global action filter provider:
public class MyProvider : IFilterProvider
{
public IEnumerable<Filter> GetFilters(ControllerContext controllerContext, ActionDescriptor actionDescriptor)
{
var rd = controllerContext.RouteData;
var controller = rd.GetRequiredString("controller");
if (string.Equals("admin", controller, StringComparison.OrdinalIgnoreCase) &&
string.Equals("logon", actionDescriptor.ActionName))
{
return Enumerable.Empty<Filter>();
}
return new[]
{
new Filter(new AuthorizeAttribute(), FilterScope.Action, 0)
};
}
}
which could be registered in Application_Start
:
FilterProviders.Providers.Add(new MyProvider());
Now if you are using some DI container such as NInject for example it supports filter binding syntax meaning that you could configure the kernel to inject the filter dynamically based on the context.
The pros of this approach is that now nomatter what controller or action is being added to your application => it will require authorization.
I would, as you suggested, move the Logon action to its own controller and apply the [Authorize] attribute to entire AdminController class. This is cleaner and will be easier to maintain in the future.
Another way that you can do this with the <location>
element in the web.config. Here's an example:
<location path="/Admin/LogOn">
<system.web>
<authorization>
<allow users="*" />
</authorization>
</system.web>
</location>
<location path="/Admin">
<system.web>
<authorization>
<allow users="?" />
<deny users="*" />
</authorization>
</system.web>
</location>
I usually do something like this:
[Authorize]
public abstract class AdminController : Controller
{
}
And inherit from that w/a naming convention:
public class UserAdminController : AdminController
{
}
vs:
public class UserController : Controller
{
}
You can do this using Filter Providers.
Phill Haack wrote about it here