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.
Related
I'm creating a site with 2 different sections (main site and admin) and both of them need authentication.
I have the main section already created and it works fine using FormsAuthentication.
now, how do I go about creating the admin section? Can I use FormsAuthentication again?
thanks
user - yes you can.
what you need to do is to create roles (such as webuser and admin) and assign the user to the appropriate role as required (you can do this either when setting the user up initially or later as an edit on their profile). anyway, getting back to the question. inside your controller, you'd then investigate the roles that existed for that logged in user and this would determine which controller actions they had access to as well as determining which view to present, should the action be 'shared' between roles.
within the controller, you can decorate the action with the following code:
[Authorize(Roles="admin")]
public ActionResult IndexAdminOnly() // you'd never have an action named this - purely to make the point
{
// your logic here
}
conversely, you could do it inside the controller:
[Authorize]
public ActionResult Index()
{
if(Roles.IsUserInRole("admin")){
// your admin logic here
}
if(Roles.IsUserInRole("webuser")){
// your webuser logic here
}
}
this is it at it's very simplest. hopefully you can google a few more links to get you over any issues that arise once you get going, or drop a note here.
Currently I use [Authorize(Roles = ".....")] to secure my controller actions on my ASP.NET MVC 1 app, and this works fine. However, certain search views need to have buttons that route to these actions that need to be enabled/disabled based on the record selected on the search list, and also the security privs of the user logged in.
Therefore I think I need to have a class accessing a DB table which cross-references these target controller/actions with application roles to determine the state of these buttons. This will, obviously, make things messy as privs will need to be maintained in 2 places - in that class/DB table and also on the controller actions (plus, if I want to change the access to the action I will have to change the code and compile rather than just change a DB table entry).
Ideally I would like to extend the [Authorize] functionality so that instead of having to specify the roles in the [Authorize] code, it will query the security class based on the user, controller and action and that will then return a boolean allowing or denying access. Are there any good articles on this - I can't imagine it's an unusual thing to want to do, but I seem to be struggling to find anything on how to do it (could be Monday-morning brain). I've started some code doing this, looking at article http://schotime.net/blog/index.php/2009/02/17/custom-authorization-with-aspnet-mvc/ , and it seems to be starting off ok but I can't find the "correct" way to get the calling controller and action values from the httpContext - I could possibly fudge a bit of code to extract them from the request url, but that doesn't seem right to me and I'd rather do it properly.
Cheers
MH
I found this on another forum and so will post it here in case anyone finds it useful. Note that how you do this changes depending on whether you are using MVC 1 or 2
the class you create needs to implement
public void OnAuthorization(AuthorizationContext filterContext)
and then you can use
string controllerName = filterContext.RouteData.Values["controller"].ToString();
and the same, substituting "action" for "controller" (make sure you check for nulls in these values first). In MVC 2 this can be changed to filterContext.ActionDescriptor.ActionName and .ActionDescriptor.ControllerDescriptor.ControllerName and you won't have to check for nulls
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
I have - I think - a complex URL to deal with in ASP MVC 1.0:
All my actions in most of the controllers require two parameters all the time: Account and Project. This is on top of each Action's requirements. This means a typical URL is like this:
http://abcd.com/myaccount/projects/project_id/sites/edit/12
In this example:
myaccount is the account name. projects can be a controller, others options are like locations, employees. project_id is the id of a project within myaccount, sites could be a controller, other options are like staff or payments. edit is an action and 12 is the id of the site edited.
(hope this is clear enough)
Now one option is to create a route and pass project_id and account into all actions of controllers by adding two extra parameters to all actions. This is not really desired and also I'm not sure the two controllers (projects and sites) are going to work here.
My ideal situation is to use some kind of context that travels with the call to the controller action and store project_id and myaccount in there. The rest of the parameters then can be dealt with in a normal way like:
// sitescontroller
public ActionResult Edit(string id)
{
string account = somecontext["account"];
string project_id = somecontext["project"];
// do stuff
}
Any ideas as to how/where this can happen? Also how is this going to work with ActionLink (i.e. generating correct links based on this context)?
Thanks!
You first need to add the tokens to your routes like {company}/projects/{project}{controller}/{action}/{id}. Then if you wrote your own IControllerFactory then it would be very easy to push the values from the RouteData into the controller via the constructor or however you wanted to do it. Probably the easiest way to get started would be to subclass DefaultControllerFactory and override the CreateController method.
This doesn't quite make sense to me. Why would you have a route that is akin to the following:
{controller}/{id}/{controller}/{id}
?
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.