The "Admin" area in my app contains a bunch of controllers, and it's a bit repetitive to put an [Authorize] attribute on all of them. Is there a way of telling the framework that all controllers in a certain area should have certain attributes?
Edit: Inheritance is not a solution in this case. First of all the controllers already inherits from a custom class, and secondly, it should be about decorating the classes, not inheriting them.
MVC 3 has a new feature called Global Action Filters which would be perfect for what you are doing. Since you're probably not on MVC 3 yet, you can also implement Global Action Filter in earlier versions of MVC by following this example. Just customize the solution to filter check if you are in the "Admin" area for the currently executing request, then apply your Authorize attribute.
This will allow you to do this without having to apply a common base class as you requested.
You could create a base controller that all controllers in this area derive from and decorate it with the [Authorize] attribute.
There are four options,
Create a separate base controller and make admin sectionsu inherit from it
Add the Authorise Attribute to the controller class instead of each method / Actrion
Decorate each on individually
write your own logic for authorization and add that to your current base controller
Related
I've been reading a lots of blogs on MVC provided here:
http://www.sitecore.net/Community/Technical-Blogs/John-West-Sitecore-Blog.aspx
However, I am not being able to explain/convience myself/team:
When to use custom control vs. out of box site core controller?
When does the Out of Box Controller gets invoked?
Benifit of custom control vs. out of box controllers?
If we go with out of box, should we include all business logic on Views. Is this testable?
I also looked at below and still not certain:
https://bitbucket.org/demoniusrex/launch-sitecore-mvc-demo
Any help will be appreciated.
Whilst I broadly agree with Kevin Obee's statement I think it's worth reminding ourselves that controllers are being used in two distinct roles in Sitecore:
Page level controller (invoked by item route)
Component level controller (invoked by redering mechanism)
When to use: Custom controller / default Sitecore controller
Page level controller
Any route that matches an item path will by default use the Index action on the Sitecore.Mvc.Controllers.SitecoreController. This action will return a ViewResult based on the layout configuration of the item.
If you have a need for changing this behaviour (e.g. something that impacts the entire page) you can specify a custom controller and action on the item (or the Standard Values for the item). For the custom controller you can either roll your own or subclass the default controller.
Component level controller
For ViewRendering Sitecore renders the Razor views without the need for a specific controller (well I guess it's the page level controller that is in play - but just imagine that Sitecore provides a default controller that gets the model using the mvc.getModel pipeline and feeds it to the Razor view).
For ControllerRendering you provide a custom controller that can execute logic (see Kevin's answer) and provide a model for the view. There is no benefit from subclassing Sitecore.Mvc.Controllers.SitecoreController.
When are controllers invoked
Page level controller
The action on the page level controller is invoked by the routing engine.
Component level controller
The action on a ControllerRendering is invoked as the page view renders.
Benefit of using: Custom controller / default Sitecore controller
The benefit of a custom controller over the default Sitecore controller is that you are in control of the logic. The benefit of using the default Sitecore controller is that Sitecore provides the logic for you.
Should we include all business logic on Views
No. (See Kevin's answer)
My personal view is that the business logic should go in command and query classes that are invoked from the controller class. From these calls you can assemble a strongly typed view model which gets passed to a dumb razor view for rendering.
Ensure that any services that the controller relies on are passed into it via constructor injection using contracts (interfaces) instead of concrete classes and you should end up with solution that is unit testable.
I've created a custom AuthorizeAttribute to add a level of permission security to my application.
[AuthorizeUser(AccessLevel="Read")]
I can decorate certain actions in my controller or just implement it on the entire class. I have certain controllers with a large number of actions with only a few that don't need this extra check. It makes more sense for me to apply this to the entire class and exclude a few actions.
How can I exclude actions from my custom AuthorizeAttribute? It seems as though every time the action is run it default to the class attribute rather than any attribute I provide for that specific action.
In ASP.NET MVC 3 I can put AuthorizeAttribute inside Global.asax's RegisterGlobalFilters, and it will apply to all controllers' actions. But how can I exclude some controller actions so these actions can be called without the user logging in?
EDIT:
Sorry, additional question, if I add authorize on the class, how can I exclude one action?
You can't do this with global filters. As their name indicates => they are global.
One way is to have all controllers that require authorization derive from a common base controller decorated with the [Authorize] attribute. Controllers that doesn't require authorization will not derive from this base controller.
Another possibility in ASP.NET MVC 3 is to write a custom IFilterProvider which based on the context will apply or not the given filters. I would recommend you reading the following blog post.
I need some data to be available on all the viewpages inside the website. The data comes from an parameter supplied to all the routes, i want to get that param and get the according data for it and make it available for all the views (including the master pages).
It would be nice if it could be done in one place.
What do i need to do to get that functionality, can it even be done?
Greetz,
Richard.
The easiest (may not be the best) would be to write a base Controller class that
1) handles one of the following events to do the job:
OnActionExecuted
OnActionExecuting
OnResultExecuted
OnResultExecuting
2) Sets the data you want to have available in ViewData.
3) Use the ViewData from your views.
4) All your Controllers must inherit from your custom base Controller.
This might not be the nicest of all approaches as I usually try to avoid inheritance like the plague, but it will work. Other options.
1) Implement it in an ActionFilter and make sure add the attribute to all ActionMethods.
2) Use something like MVC Turbine to define ActionFilter's that trigger for all ActionMethods in you ASP.NET MVC Application.
If I've applied an authorisation attribute at controller level, is it possible to override this on one of the methods on that controller?
Thanks
James
That depends upon what kind of "override" you want. You cannot remove the attribute which is on the class, but you can add the attribute to the method again in order to make things more restrictive.
Update in response to comments. First, making your own AuthorizeAttribute is somewhat dangerous. AuthorizeAttribute contains code which interacts with the caching attributes in order to ensure that the cache cannot serve protected content to a non-authorized user. At a minimum, you should subtype the existing AuthorizeAttribute rather than creating something wholly new. Generally, however, it's a better idea to use the existing AuthorizeAttribute and specialize your authorization by creating a new/finding an existing ASP.NET membership provider.
I don't think it would be good design to have a filter on an action which "overrides" a filter on a controller. However, you could change the design of the filter on the controller to not require authorization on an action of a certain name. You could, for example, override the AuthorizeAttribute.AuthorizeCore method to test for an action name in the same way the existing method tests for the user name and the roles. Take very careful note of the comments in this method regarding thread safety.
I'm not sure if this is exactly the same question, but it may help...
How to make ActionFilter on action method take precedence over same ActionFilter on controller