Hiding Role Based Security Html.ActionLinks

I use @RenderSection("Contextual", false) in my _Layout.cshtml to allow various views to display their specific content there. Some do not, others do.

In addition, I use role-based protection and ActionFilter to control whether a particular user has access to certain controller actions and thus the routes on my site.

What I would like to do is provide an @RenderSection("Contextual", false) section on my _Layout.cshtml, and then indicate that a particular page provides any contextual material for that page and have an appropriate controller handle to check if the user to perform some action and maybe even see that there are options, but I'm not sure I think about it correctly. Here's how to do it now:

Now I have a section in one of my Index.cshtml files, for example:

 @section Contextual { <div>@Html.ActionLink("Create New", "Create")</div> <div>@Html.ActionLink("Generate Report", "Report")</div> <div>@Html.ActionLink("Other Stuff", "Other")</div> } 

and then in my respective controller I have something like this:

 [Authorize(Roles = "Editor")] public ActionResult Create() { // stuff } 

This will work the way I want (no editors will be able to create new elements), but the Create entry is available to everyone. I can do something like this:

 @section Contextual { @if (User.IsInRole("Editor")) { <div>@Html.ActionLink("Create New", "Create")</div> } <div>@Html.ActionLink("Generate Report", "Report")</div> <div>@Html.ActionLink("Other Stuff", "Other")</div> } 

And it works pretty well, hiding the Create link from non-editors, but I am on a worry about whether it is good or not to do this, and I see that along the way I have a situation where the rules change, and then I have two places for synchronization: the controller action attribute and the code in the view.

Is this a smart approach? Is there a better way to approach this?

+6
source share
2 answers

I like to use flags that are more explicit for the view model, which are populated on the controler.

For instance:

  // on the controller viewModel.CanCrete = User.IsInRole("Editor"); // ...snip... return View(viewModel); } 

So, you need to add this flag to your view model or, possibly, to the base class of view models. You can follow the path of creating a custom action filter to populate it on multiple controllers or do some processing in the base class of the controller.

I would also like to define a convenient extension method:

 public static string If( this string s, bool condition ) { return condition ? s : String.Empty; } 

Depending on which APIs you are using, you may also need the MvcHtmlString extension.

Then in the view:

 @section Contextual { <div>@Html.ActionLink("Create New", "Create").If(Model.CanCrete)</div> <div>@Html.ActionLink("Generate Report", "Report")</div> <div>@Html.ActionLink("Other Stuff", "Other")</div> } 

You can decide what you would like to do with the div , maybe you will have another helper that wraps the links in the div, or maybe you can use CSS to achieve any visual layout that you are going to use.

+8
source

I like @TJB a lot and think that I really will do something similar. But if you want to go the other way ... you can create your own LinkExtensions that overload standard LinkExtensions.

 public static class MyLinkExtensions { public static MvcHtmlString ActionLink(this HtmlHelper htmlHelper, string linkText, string actionName, YourAccessStuff access) { if(access.Has(actionName)) { ActionLink(htmlHelper, linkText, actionName); } else { // Maybe only show the link text as if it disabled and not a link? // Maybe do nothing? } } } 

Suppose that "YourAccessStuff" is actually implemented. This will centralize these access checks, rather than stick to them on every ActionLink. The disadvantage, obviously, is that you can still forget to check your safety. Using some kind of dependency injection will also make it more enjoyable.

0
source

Source: https://habr.com/ru/post/887393/


All Articles