I know it is possible to decorate a controller with the Authorize attribute to control access, what I don't know is the accepted or proper way to enforce security across all the controllers/views in an Area.
Is there something in web.config, area registration or some other place to apply authorization security?
A convenient way is to create a new base class
[Authorize]
public abstract class AuthorizeBaseController : Controller
{
}
and make sure that all of your controllers for which you require authorization (in your case, everything in the area that you're concerned about) descend from AuthorizeBaseController.
public class HomeController : AuthorizeBaseController
{
public ActionResult Index()
{
return View();
}
}
The [Authorize] attribute should affect all of the descendents of the new base class.
Edit The issue that I have with using the <location path="" > approach is that, since the routing engine makes it's possible for any route to call any controller, setting authorization based on the url (and thus a specific route) instead of the controller actions makes it possible to call a controller that should be protected and skip the authorization. That wasn't an issue in webforms since a page was a page (and not a method call), but the separation between page/path and code in MVC makes this a huge security hole.
The only safe way of doing this in an MVC application is to do what David suggests - attributing a base controller and having all controllers in the area subclass that base controller.
Using a <location> tag for authorization in MVC will open security holes in your application. You're not interested in securing URLs or routes. You want to secure the controllers themselves, since they're the actual resources you're trying to protect. Therefore the protections need to be placed directly on the controllers.
Furthermore, remember that an area is really just a fancy way of grouping routes, not controllers. Trying to use fancy logic to detect the current area and infer authorization settings will also open security holes in your application.
As was already suggested, you can make use of the <location /> element in your web.config. Otherwise, you can use a base controller class per-area and decorate that with the AuthorizeAttribute so that all controllers which inherit from it are also filtered.
you can always use
<location path="" >
<system.web>
<authorization>
deny or allow
</authorization>
</system.web>
</location>
Related
I've been using attribute based routing in an API controller project:
[HttpGet("products/{productId}")]
public async Task<IActionResult> ProductDetails(int productId) {
...
return Ok(someModelToSerializeAsJson)
}
This works great. I find it much clearer and less error prone than conventional route table based routing. I kept wondering why we can't use attribute based routing for MVC (non API) projects with views. I tried this for a small non public facing web application and it seems to work great. You have to mark the controllers as [ApiController], but you can still return views in the action methods.
[HttpGet("products/{productId}")]
public async Task<IActionResult> ProductDetails(int productId) {
...
return View(someModelForTheView)
}
I would like to make the same choice for our main public facing website, but I'm worried I'm missing a solid reason why I shouldn't.
As an example, I've noticed that API controllers aggressively turn off HTTP caching (e.g. cache-control: no-cache, no-store). That is a quirky issue to work around.
Are there other clear reasons to NOT use attribute based routing with MVC views?
Looks like I was wrong. You can use attribute based routing for regular MVC controllers. I thought you had to use an ApiController, but that is not correct. The trick for me was that the base class for my controller needed to be Controller instead of BaseController. Shout out to #Métoule for that nugget of information.
I am learning ASP.NET MVC3, and I just created a controller for my Model/Context. However, anyone can navigate to these pages and use them. How can I set permissions for these pages?
AuthorizeAttribute will be your first line of defense. You can grant access based on group membership or username. Works a lot like Code Access Security / Principal Permission Attributes but isn't as hard to work with.
Example:
// Allow Everybody in
public ActionResult Index ()
{}
// Allow only Editors/Admin to post edits back to controller.
[HttpPost]
[Authorize(Roles="Admin,Editor"]
public ActionResult Edit(EditViewModel vm)
{}
You can use them at the Class or Method Level, so you can have a base controller that only lets authorized users use certain controllers of your app.
If you find yourself using the same groups or users over and over, I would create an override of the AuthorizeAttribute that has those groups predefined, that way you don't misspell or forget them. This will also DRY up your code, which is always great.
You can use the Authorize attribute to rstrict permission, often this is inherited from the base class. This is the most common and recommended.
You can use the ActionFilter attribute and override the OnActionExecuting and set custom logic in there. Possible, but not the recommended.
There are lots of other ways too, but the prior two are the main ones for MVC.
I was having trouble getting AuthorizeAttribute to work for an out of the box MVC 3 app. So I created my own attribute inheriting from AuthorizeAttribute and overriding all methods and adding break points to see what was going on.
But the problem is, the attribute code is NEVER called! Any idea what could cause this?
It is a completely blank MVC 3 app with a HomeController with an Index method. Form authentication set to redirect to ~/Account/LogOn. But it seems it just doesn't load the [Authorize] attribute...
EDIT:
Sorry guys, I must really be tired today :) it is in fact not a totally blank project. I have some Ninject code that provides a repository to my HomeController. If I disable this and create a parameterless constructor on the HomeController the AuthorizeAttribute seems to work ok.
Any idea why Ninject dependency injection would interfere with the Authorize attribute?
--
Christian
And you say you have the following settings on your web.config?
<authentication mode="Forms">
<forms loginUrl="~/Account/logOn" />
</authentication>
How are you using your authorize attribute, could we see some code?
You might want to take a look at this step by step "Authenticating Users with Forms Authentication" guide.
Creating a custom attribute and enabling Forms auth aren't enough. You still have to decorate the Controllers / Actions for which you want the filter to execute with the attribute.
[Authorize]
public ActionResult Index()
{
// ...your code
}
or
[Authorize]
public class HomeController
{
// ... your actions
}
Edit As tpeczek said in comments, also ensure that [Authorize] is using your AuthorizeAttribute and not System.Web.Mvc.AuthorizeAttribute.
I have a ASP.NET MVC site that is locked down using Forms Authentication. The web.config has
<authentication mode="Forms">
<forms defaultUrl="~/Account/LogOn" loginUrl="~/Account/LogOn" timeout="2880"/>
</authentication>
<authorization>
<deny users="?"/>
</authorization>
None of my pages other than Account/LogOn can be viewed unless the user is authenticated.
Now I am trying to add PayPal IPN to my site and in order to do that I need to have two pages that handle PayPal's payment confirmation and thank you page. These two pages need to be available for anonymous users.
I would like these pages to be controller actions off my Account controller. Is there any way I can apply an attribute to specific action methods that make them available to anonymous users? I found a several posts here that attempt to do that but there was most people wanted the opposite scenario.
Basically I want may AccountController class to have no authorization for most of the methods except for a few. Right now it looks like only the LogOn method is available to anonymous users.
Yes you can. In your AccountController there's an [Authorize]-attribute either on class-level (to make the whole controller restricted) or on specific methods.
To make specific actions restricted you simply use the Authorize-attribute on the methods that handle these actions, and leave the controller-class unrestricted.
Here are a few examples... hope it helps
To require users to login, use:
[Authorize]
public class SomeController : Controller
// Or
[Authorize]
public ActionResult SomeAction()
To restrict access for specific roles, use:
[Authorize(Roles = "Admin, User")]
public class SomeController : Controller
// Or
[Authorize(Roles = "Admin, User")]
public ActionResult SomeAction()
And to restrict access for specific users, use:
[Authorize(Users = "Charles, Linus")]
public class SomeController : Controller
// Or
[Authorize(Users = "Charles, Linus")]
public ActionResult SomeAction()
As you can see, you can either use the attribute at class-level or at method-level. Your choice!
I don't think there is an "Unauthorize" attribute that can be applied to actions and if you don't want to place "[Authorize]" on all but two actions in a controller try the following:
Here are two methods I can think of:
1- Location attribute in Web.config (Not sure if this will work with MVC routing etc.)
After your
<system.web> stuff </system.web>
in web.config file, add the following:
<location path="Account/ActionOne">
<system.web>
<authorization>
<allow users ="*" />
</authorization>
</system.web>
</location>
Where Account/ActionOne is the name of the action method you want to give anonymous access to. For the second Action, copy the above code and paste it right after it and change the name of the Action.
I'm not sure if this will work because of MVC routing etc, but give it a try.
2- Base Controller
If the previous solution didn't work, your best bet would be to create a base controller that is decorated with the Authorize attribute:
[Authorize]
public class AuthorizeControllerBase : Controller {}
Then have all your controllers inherit from it:
public class AccountController : AuthorizeControllerBase
{
// your actions etc.
}
This will make any controller that inherits from AuthorizeControllerBase require authorization/logging in to invoke any methods.
Then you would need to remove from your web.config
Instead of securing all resources on your website by default and then looking for a way to provide anonymous access for individual resources, you're probably better off taking the opposite approach. Don't specify authorization rules in your web.config, then use Authorization filters (see Mickel's answer) to secure individual controllers and/or actions.
I want to make the roles default for my controller class to "Administrators, Content Editors"
[Authorize(Roles = "Administrators, Content Editor")]
I've done this by adorning the controller with the attribute above. However, there is one action that I want to be available to all (namely "View"). How can I reset the Roles so that everyone (including completely unauthorized users) have access for this action.
Note: I know I could adorn every single action other action with the authorize attribute above but I don't want to have to do that all the time. I want all of the controllers actions to be unacessible by default so that if anyone adds an action they have to make a considered decision to make it available to the general public.
MVC4 has a new attribute exactly meant for this [AllowAnonymous]
[AllowAnonymous]
public ActionResult Register()
http://blogs.msdn.com/b/rickandy/archive/2012/03/23/securing-your-asp-net-mvc-4-app-and-the-new-allowanonymous-attribute.aspx
You can place the Authorize attribute on the action methods. Not just at the class level.
So, move the attribute from the controller class to just the action methods you want to secure.
The only solution I can think of so far is to create and register another controller so that I have one for anonymous access, and one for authorized access but that's not quite as elegant as I would have liked.