Is there any way how to display only links with access, which is defined by
[Authorize(Roles = "SomeRole")]
In controller?
I don't believe that there is a simple solution for doing this without reflecting the Controller, getting the Action and checking the AuthorizationFilters.
With that said, an alternative solution might be to create an extension method overload for Html.ActionLink which takes a role name and check User.IsInRole(roleName). Only output a link if the user has access to the specified role.
Something like this:
public static class EntentionMethods
{
public static MvcHtmlString ActionLink(this HtmlHelper helper, string linkText, string actionName, string controllerName, string roleName)
{
if (!helper.ViewContext.RequestContext.HttpContext.User.IsInRole(roleName))
{
return MvcHtmlString.Empty;
}
return helper.ActionLink(linkText, actionName, controllerName);
}
}
Not a pretty as you might have been hoping, but this often serves the needs.
The Authorize attribute should be placed on the controller actions (or controller itself) in which you want to restrict access to, this way you are enforcing only users with the role of "somerole" to access the links generated to and from the controller actions. The links you want to restrict access to will not be displayed from the Authorize attribute. It simply restricts and enforces users in roles, or users with certain names to access the actions, if roles and users are explicitly defined.
One possibility is to use MvcSiteMapProvider for your menu. It has a built-in security trimming feature that automatically hides links according to AuthorizeAttribute. If you don't like the built-in HTML helpers, you can customize the templates and/or build your own HTML helpers that hide links based on node accessibility.
Or, if you don't want to use a 3rd party library, you can reverse engineer the AuthorizeAttributeAclModule to make your own implementation.
Full Disclosure
I am a major contributor of MvcSiteMapProvider.
Related
<authentication mode="Forms"/>
<forms loginUrl="~/Login/Login" defaultUrl="~/Login/Something" protection="All"></forms>
</authentication>
One way is to add Authorization tag to every Controller to secure application from unauthorized user.Other way is to use session variables to store user information .So,my question is from hacking perspective,how we can secure our application form hackers?Is there any other way to secure application?
Note: This answer should not be viewed as complete in any way, security is always hard and if it's important to you - always consult with a third party security and penetration testing company. This is just a few things you should consider to make your MVC application safer (not necessarily safe).
Protect controllers
First step is to apply [Authorize] to your controllers, to make sure there is some sort of valid authentication before accessing a method. Preferably add Authorize to all controllers and make exceptions with [AllowAnonymous] to make it secure-by-default.
Authorize the user
Even though you've added [Authorize] to the controller, that only means that the user is logged in, not that the user should have access to whatever method is being accessed. The first step is to extend the attribute by specifying a set of roles for the method: [Authorize(Roles = "Administrator"]
Depending on the application, you might also have to check that the current user belongs to the company/group or whatever that is being edited, to prevent someone from modifying data that doesn't belong to them.
Don't leak data into models
If you're using your actual data models as view models, you are at risk of allowing the user to enter more data than they're supposed to. Consider this example:
class Employee {
public int Id { get; set; }
public string Name { get; set; }
public int Salary { get; set; }
}
Assume that we for some reason allow our employees to change their name, and we use the Employee model for that. But of course we only make an edit field for the Name, and not the Salary as that would be stupid :D. However, due to how the model-binding works, a smart Employee could just add <input type="text" name="Salary" value="2147483647"> to the form when changing their name, and our gullible db.Entry(employee).State = EntityState.Added; followed by db.SaveChanges() would update their salary as well.
Solution? Either make a view model with only the Id and Name property, to make it impossible to change the salary, or use the Bind attribute to only include the properties we allow: public IActionResult Update([Bind(Include="Id,Name")]Employee model).
XSS
A very important part is to protect your users from bad dynamic content. If I can enter HTML and Javascript that's displayed to some other user, I can abuse that to steal their authentication token for example. Making to never render user-entered data as HTML is a first step to prevent this. You should also make sure to always use the anti-forgery token in your forms as well. Adding CSP headers is a good practice to prevent someone from injecting scripts that shouldn't be there.
I am developing an MVC app to serve multiple domains - each is a branch of a larger company.
A LocalBranch class stores details such as phone, address, email, location coordinates etc.
I want to create a single instance of this class per http request and have it available throughout the application - from within controllers, views, some helper classes and other code.
Is there a recommended way of doing this?
Right now I have it as a property on a BaseController and use ViewBagto pass it to views. But I would prefer it strongly typed in Views if possible.
I don't want to put it in an application variable, because we need to serve different values to different domains.
I would rather avoid a session variable if possible because we might scale up to use multiple servers in the future, and I've heard this doesn't play well with sessions.
Please feel free to update tags / title if you think there is a clearer way of expressing what I'm after. Thank you.
The best way to maintain your state in a web application per request is simply use the HttpContext class.
You need to store your state(LocalBranch) as an Item in the HttpContext:
HttpContext.Current.Items.Add("LocalBranch", GetLocalBranch());
You can fetch the Item all across your application like this:
LocalBranch branch = HttpContext.Current.Items["LocalBranch"] as LocalBranch;
The Items property is simply a key value Dictionary. The value is an object. You will have to check for nulls and this is really similar to the Session object you know. The main difference is the scope. The HttpContext is a dot net object that has a lifetime of an http request.
Now using the HttpContext the way I've shown you is the simplest way to do it.
You can go two steps forward and use a framework called Unity and add a lifetime to your objects.
Unity does much more and the lifetime management is just one gem.
You can create a custom HttpContext lifetime that generates objects per request. Something like this.
And them all you need to do is:
1.Register you LocalBranch class with the HttpContext lifetime.
2.Add a static Current property which will use the Unity container and resolve the correct instance of LocalBranch.
3.Use it something like this: LocalBranch.Current
BTW, you can use Unity's dependency injection for injecting objects into controllers and other modules. That's a better practice then just using the static Current property.
You kind of have two questions here. The first is "How do I create a single instance of this class per HttpRequest?" The second is "How do I make this available to strongly typed views?"
The first has pretty much been answered by #amir-popovich to use dependency injection. However, FWIW I would probably use Ninject instead of Unity (just preference, really) and I would probably implement it differently. I would not use HttpContext, and simply build a service (which is instanciated using Ninject's OnePerHttpRequest Module, passing the domain as an argument to get the proper values).
Then, in order to add these LocalBranch values to your strongly typed View Model, you can first create a base view model which holds this type:
public class BaseViewModel
{
public LocalBranch Branch {get;set;}
}
Then, make all of your current view models inherit this base type
public MyViewModel : BaseViewModel
{
public string SomeValue {get;set;}
}
Then in your controller, it is easy enough to add these values from the service you created from the first step
public ActionResult SomeAction()
{
var vm = new MyViewModel();
vm.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
//do other stuff
return View(vm);
}
However, that gets pretty tedious to add that to each controller action, so you can instead create a Result Filter to add it for you:
public class LocalBranchResultFilter : FilterAttribute, IResultFilter
{
public void OnResultExecuting(ResultExecutingContext filterContext)
{
//This method gets invoked before the ActionResult is executed.
filterContext.Controller.ViewData.Model.Branch = LocalBranchService.GetLocalBranchValues(); //Local Branch Service has been injected with Ninject
}
}
Now, you can just decorate your Controller and/or Actions with the filter (you could even set it in the Global Filters if you want).
You can embed the child actions into your layout or a view. You can even cache its output so you don't keep re-querying the database.
controller
[ChildActionOnly]
[OutputCache(Duration=500, VaryByParam="*")]
public ActionResult Info()
{
var localBranch = db.GetLocalBranch();
return PartialView("_Info", localBranch);
}
_Info view
This bit will get inserted into your other views
#model LocalBranch
<span>#Model.address</span>
<span>#Model.phone</span>
Use in _Layout or other view
<p>lorem ipsum...</p>
#Html.Action("Info")
I have a class used by controllers at [Project].Controllers and by controllers at different areas. How could I determine where the controller is at? (I guess I could look at the HttpContext.Current.Request's properties -but I am looking for a "proper" MVC way). Thank you.
That is:
[Project].Helpers // called by:
[Project].Controllers
[Project].Areas.[Area].Controllers
// how could I determine the caller from [Project].Helpers?
We purposefully did not expose a way to get the current area name from an MVC request since "area" is simply an attribute of a route. It's unreliable for other uses. In particular, if you want your controllers to have some attribute (think of the abstract term, not the System.Attribute class) which can be used by the helper, then those attributes must be found on the controllers themselves, not on the area.
As a practical example, if you want some logic (like an action filter) to run before any controllers in a particular area, you must associate the action filter with those controllers directly. The easiest way to do this is to attribute some MyAreaBaseController with that filter, then to have each controller that you logically want to associate with that area to subclass that type. Any other usage, such as a global filter which looks at RouteData.DataTokens["area"] to make a decision, is unsupported and potentially dangerous.
If you really, really need to get the current area name, you can use RouteData.DataTokens["area"] to find it.
You should be able to get the area string from RouteData:
// action inside a controller in an area
public ActionResult Index()
{
var area = RouteData.DataTokens["area"];
....
return View();
}
.. so you can make an extension method for helpers like this:
public static class SomeHelper // in [Project].Helpers
{
public static string Area(this HtmlHelper helper)
{
return (string)helper.ViewContext.RouteData.DataTokens["area"];
}
}
I've come across two recommendations for creating custom html helpers: either extend an existing one, or write your own class.
I'd prefer to keep my custom code separated, it seems a bit sloppy to extend helpers for a decent-size application.
But the benefit I see in extending is that 'This HtmlHelper helper' is passed as a parameter, through which I can get ViewContext.HtmlContext.
My question is, how can I roll my own helper class and still have ViewContext.HtmlContext available to me?
Thanks!
Edit:
What I am looking to do, is create "MyHelperClass" which will render some custom objects as html. I don't see a need to "Extend" an Html helper since i'm not using anything that it offers me. The only reason I have to extend htmlhelper currently is to access httpcontext, as you've shown. But my question was, how can i access httpcontext in my own class, without extending an existing helper. thanks
public static class HtmlHelperExtensions
{
public static HttpContextBase GetContext(this HtmlHelper htmlHelper)
{
return htmlHelper.ViewContext.HttpContext;
}
}
You might also use: System.Web.HttpContext.Current.Request.RequestContext
Let's say I have a controller action that is restricted to only certain users, like this:
[Authorize(Roles="somerole")]<br />
public ActionResult TestRestricted() {
return View();
}
On a view, that is public to everyone I have a link to the action defined above:
<%= Html.ActionLink("Click here!", "TestRestricted") %>
What I'd like to do is hide the link for everyone that is not allowed perform the "TestRestricted"-action. Is there a way to check if the current user is authorized to use the corresponding action? Without defining any additional or duplicate access rules in addition to the authorization filter?
There is nothing in the MVC framework that can control permissions at such a granular level.
First Approach
This is by far the easiest approach. The drawback is having to assign the role to each action link.
What you could do, is write a Action HtmlHelper to control the permissions at a link level. Make sure you include the namespace System.Web.Mvc.Html.
public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, string role)
{
MvcHtmlString link = new MvcHtmlString(string.Empty);
if (htmlHelper.ViewContext.RequestContext.HttpContext.User.IsInRole(role))
{
link = htmlHelper.ActionLink(linkText, actionName);
}
return link;
}
<%= Html.ActionLink("Click here!", "TestRestricted", "somerole") %>
Second Approach
You could use reflection to discover the action(method) being called. Once discovered a simple check of the attributes would tell you if the authorize attribute was present and what role it was set too.
This may help: http://weblogs.asp.net/rashid/archive/2009/09/06/asp-net-mvc-and-authorization-and-monkey-patching.aspx
I am also trying to find an answer to this question.....