Permissions within views in ASP MVC - asp.net-mvc

I understand that you can use forms authentication to grant/deny access to certain pages based on the criteria of your choosing.
However I wish to go in a little more specific than that and say, have different buttons appear for users based on thier permissions.
I know I could do something like
if(((User)ViewData["CurrentUser"]).IsEmployee).....
But that doesn't seem very elegant and could get messy very quickly.
Are there any guidelines/tools/framework features that could help me out here?

Use role-based authentication, then set roles appropriately. Then you can do things like:
if (ViewContext.HttpContext.User.IsInRole("vEmployee") {
The advantage of this is that it's core ASP.NET functionality -- not even MVC-specific -- so it's going to work with every possible membership provider.
Then you can add a view helper overload for whatever control you want to conditionally display:
public static string TextBox(this HtmlHelper helper,
string name, string value, string role, object htmlAttributes)
{
if helper.ViewContext.HttpContext.User.IsInRole(role) {
return helper.TextBox(name, value, htmlAttributes);
}
else
{
return null;
}
}
...and call it:
<%= Html.TextBox("name", "value", "vEmployee", null) %>

I had the same issue a while ago for a WPF application. It could work for ASP.NET as well.
For every "button" (UserControl in WPF) you set by attribute the role needed to execute its functionality.
At the begninning of your Action, you create a list of all the "Buttons" that require a special authorization.
Before calling the "return View()" you call a functions that iterate all you special "Buttons" and sets is visibility based on the role of the user.
For WPF that works because you can't call the method by a get/post request... For the web you should make something more sophisticated not just hide/show the button...
I hope this gives you at least a clue... It worked pretty fine for my implementation, but it was just a prototype...But I think I'll use it in future.
PS: Sample code can be found here

Don't do it. Use the controller for that kind of logic

Related

ASP.NET WEB API Multiple Interfaces

I am designing ASP.NET WEB API service with two interfaces. First interface is read-only and second one is admin interface (to be used with MVC app) with full CRUD support. Does anyone know where I can get more information on such setup, any tutorials, walk thought, sample design document?
Also does it worth splitting these into two interfaces or keep them in same? But problem is that in read-only I expose 2-3 properties for every object while for admin there are 10-15?
Similar setup for WCF or design spec will do.
If I understand what your wanting to do, may I suggest rather than having one URL, i.e. \dataStuff\dataView split off into another view, maybe \dataStuff\edit\ which only admin's have access to, can be done like so:
[Authorize(Roles = "Administrators")]
public ActionResult edit()
{
return View();
}
then next to each data element that your viewing add the following to ONLY admin's through use of User.IsInRole
#foreach (var item in Model.dataTable)
{
#*
write a value from the data table here
*#
#Html.ActionLink("edit", "edit").If(User.IsInRole("Administrators"))
<br/>
}
obviously you don't have to display your data like this, I'm just showing that you add to the end of each element of the database an edit ActionLink IF the user is admin.
This allows your admin to view data like a user and also have the added functionality they need. Code re-use is better than a single view which has two states, Admin and non Admin.
Sorry if this isn't the best explanation, fairly new to MVC
Seems like this concept is sometimes called CQRS.
Sample: http://martinfowler.com/bliki/CQRS.html

How to keep views free of authorization logic in mvc?

I have a view to display a list of items.
The user can edit, delete or create new items, but according to their authorizations they may or may not be allowed to do some of this actions.
I have the requirement to display only the actions which the current user is allowed to do, but I don't want to clutter the views with authorization if-else's
Despise of being a very common requirement, I can't find a real satisfactory way of doing it.
My best approach so far is to provide an overload to the Html.ActionLink extension method that takes the permission to ask for, but there are going to be more complex scenarios, like hiding entire blocks of html or switching a textbox for a label+hidden.
Is there a better way to do this?
One example I can think of would be to invoke Html.RenderAction (link: http://msdn.microsoft.com/en-us/library/ee703490.aspx), and then pass the link you wish to use as a route value to the action method (it will appear as a parameter on your action). Because it's a RenderAction, you get to go back through the controller process and thus you can have it render whatever view or data you want depending on the user's state.
Example:
<% Html.RenderAction("Permissions" /* Controller */, "PermissionLink", new { Url = "Admin.aspx" }); %>
And your controller would have something like:
public ActionResult PermissionsLink (string url)
{
// Do Whatever kind of Authentication you want here, Session is available, etc
if(authenticated)
return View("Link");
else
return View("Blank");
}
I personally don't see anything wrong with this kind of conditional logic within the view. The logic is still all about presentation. You decide whether to show or hide, enable or disable, highlight etc. This is the view's job, not the controller's. As long as the view does not need to actually compute anything to come to its decision. The controller should be agnostic of the view's implementation as much as the other way around.
My approach would be this:
Make the controller do the actual logic of deciding the level of access that the user has
Pass this information to the view using (ie via the ViewModel) but in a way that is neutral to implementation details (ie "user is admin", "user is an author", etc)
Let the view draw itself appropriately using only the pre-compiled data that is provided by the controller.
This also has the benefit that the view itself can be removed and replaced without any effect on the controller.
We had this same issue. We ended up writing a lot of helper methods and in cases where lots of html output was required, we put them in partial views.
Would it not be simpler to create several views with various controls based on what values the user can change, then return the right view based on the user's access rights?
Views should be for presenting information only, there really shouldn't be any conditional logic contained in them, or at least a bare minimum.
I've always found when i get to the point where i'm finding it hard to grok a situation like yours, the best solution is always to go back to the controller and e-assess what i'm passing to the view in the first place.
By the time the View is called to do its job, all of the important 'decisions' should have already been made.
In complicated situations where are many conditions and rules I'm doing this that way:
ViewModel
public class ModelView
{
private IAuthorisationService { get; set; }
public bool CanShow
{
...
}
}
View:
<% if(Model.CanShow) { %>
<html>
<% } %>

ASP.NET MVC - Extending the Authorize Attribute

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

lessons learned or mistakes made when using asp.net mvc

what are your top lessons learned when starting asp.net mvc that you would highlight to someone starting out so they can avoid these mistakes?
Use Html.Encode() everywhere you print data, unless you have a very good reason to not do so, so you don't have to worry about XSS
Don't hardcode routes into your views or javascripts - they're going to change at some point, use Url.Action() instead
Don't be afraid of using partial views
MVC is no silver bullet, first evaluate if it's indeed the best tool of choice for solving your problem.
Don't forget the "Unit Tests" part of the pattern.
Try to always use a ViewModel to pass data between the Controller and the View.
You may think you don't need one, you can just pass your model around, but suddenly you need a list box with several options for editing a model, or displaying a message (not validation message) and you start adding items to the ViewData, with magic strings as keys, making the app harder to maintain.
There are also some security issues that you solve with a ViewModel.
For instance:
class user:
int id
string name
string email
string username
string password
Your view let's the user change his name and email and posts to the action
public ActionResult Edit(User user)
{
--persist data
}
Someone could tamper your form and post a new password and username and you will need to be very careful with the DefaultBinder behavior.
Now, if you use a ViewModel like:
class userEditViewModel:
int id
string name
string email
The problem is gone.
Whenever it is possible make your view typed
Avoid logic in your views
stay away from the HttpContext
Get Steve Sandersons Pro ASP.NET MVC Framework
Debug into the Sourcecode
If you make a Controller method with a different parameter name from id for a single parameter method, you have to make a new route. Just bite the bullet and use id (it doesn't care about the type) and explain it in the comments.
Makes sure you name your parameters with RedirectToAction :
return RedirectToAction("DonateToCharity", new { id = 1000 });
You lose your ViewData when you RedirectToAction.
Put javascript in seperate files, not into the view page
name of the controller :)
unit test Pattern
Don't use the Forms collection, use model binding.
Try not to use ViewData, create a ViewModel.
If you have a loop or an if in your View, write an HTML helper.
Kindness,
Dan
Don't let your controller become a fat one and do too much work. I've seen 1000+ line controllers in the past and it just becomes an absolute nightmare to understand what's going.
Utilise unit testing for your controllers to ensure that dependencies are kept under control and that your code is testable.
Don't get drawn into letting jQuery and fancy clientscript define the behaviour of your application, try and use it as sparingly as you can and let it enhance your application instead.
Use partial views and HTML helpers whenever possible to ensure that your Views do not become unwieldy and a maintenance nightmare.
Use a ViewModel whenever possible.
Use a dependency injection framework to handle your dependencies (MvcContrib has several controller factories, though it's simple enough to roll your own).
Use a different controller for every section of your site (e.g., Home, Account)
Learn how to use ViewData and TempData
Learn what's the use of RenderPartial

Using a filter to execute a different action?

I want to avoid having lots of if Request.IsAjaxRequest() in my controllers. I was thinking that if I could condense this logic to an ActionFilter it would be easy to adopt a convention in my application to provide a second action for any request that may use Ajax, while providing a fall back if JavaScript is disabled.
public ActionResult Details(int id)
{
// called normally, show full page
}
public ActionResult Details_Ajax(int id)
{
// called through ajax, return a partial view
}
I initially thought I could do something like this:
public class AjaxRenameAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.RouteData.Values["action"] = filterContext.RouteData.Values["action"] + "_Ajax";
}
But that won't work because the action to invoke is decided and then the Filters on it are processed.
I don't really want to return a RedirectResult each time someone calls an action, it seems a bit pointless to double the amount of Http requests.
Is there a different way to route through the request to a different action? Or is what I'm doing unadvisable and I should look for a better way of doing things?
Cheers
How about AcceptAjaxAttribute in MvcFutures?
AcceptAjaxAttribute is probably is what's needed here; however, I'd like to propose a different way of thinking about this problem.
Not all Ajax requests are equal. An Ajax request could be trying to accomplish any of the following things:
Binding JSON data to a rich grid (like jqGrid);
Parsing/transforming XML data, such as an RSS feed;
Loading partial HTML into an area of the page;
Asynchronously loading a script (google.load can do this);
Handling a one-way message from the client;
And probably a few more that I'm forgetting.
When you "select" a specific "alternate action" based solely on the IsAjaxRequest method, you are tying something very generic - an asynchronous request - to specific functionality on the server. It's ultimately going to make your design more brittle and also make your controller harder to unit test (although there are ways to do it, you can mock the context).
A well-designed action should be consistent, it should only care what the request was for and not how the request was made. One might point to other attributes like AuthorizeAttribute as exceptions, but I would make a distinction for filters, which most of the time describe behaviour that has to happen either a "before" or "after" the action takes place, not "instead of."
Getting to the point here, the goal stated in the question is a good one; you should definitely have different methods for what are correctly described as different actions:
public ActionResult Details(int id)
{
return View("Details", GetDetails(id));
}
public ActionResult JsonDetails(int id)
{
return Json(GetDetails(id));
}
public ActionResult PartialDetails(int id)
{
return PartialView("DetailTable", GetDetails(id));
}
And so on. However, using an Ajax action selector to choose between these methods is following the practice of "graceful degradation", which has essentially been superseded (at least IMO) by progressive enhancement.
This is why, although I love ASP.NET MVC, I mostly eschew the AjaxHelper, because I don't find that it expresses this concept that well; it tries to hide too much from you. Instead of having the concept of an "Ajax Form" or an "Ajax Action", let's do away with the distinction and stick to straight HTML, then inject the Ajax functionality separately once we're certain that the client can handle it.
Here's an example in jQuery - although you can do this in MS AJAX too:
$(function() {
$("#showdetails").click(function() {
$("#details").load("PartialDetails", { id: <%= Record.ID %> });
return false;
}
});
This is all it takes to inject Ajax into an MVC page. Start with a plain old HTML link and override it with an Ajax call that goes to a different controller action.
Now, if on somewhere else on your site you decide you want to use a grid instead, but don't want to break the pages using partial rendering, you can write something like this (let's say you have a single master-detail page with a list of "orders" on the left side and a detail table on the right):
$(".detaillink").click(function() {
$('#detailGrid').setGridParam({
url: $(this).attr("href").replace(/\/order\/details/i,
"/order/jsondetails")
});
$("#detailGrid").trigger("reloadGrid");
});
This approach completely decouples client behaviour from server behaviour. The server is effectively saying to the client: If you want the JSON version, ask for the JSON version, and oh, by the way, here's a script to convert your links if you know how to run it. No action selectors and finagling with method overloads, no special mocking you have to do in order to run a simple test, no confusion over which action does what and when. Just a couple of lines of JavaScript. The controller actions are short and sweet, exactly the way they should be.
This is not the only approach. Obviously, classes such as AcceptAjaxAttribute exist because they expected some developers to use the request-detection method. But after having experimented quite a bit with both, I find this way much easier to reason about and therefore easier to design/code correctly.

Resources