4

In MVC 5 I am attempting to use the controller to render a partial view only if the (Windows Authenticated) user belongs to one or more of a list of Active Directory groups. There are over 30 distinct groups I need to account for, so the "hello world" examples don't fit my needs. After playing scavenger hunt on the web, I managed to collect this much. No compile or runtime errors, but the content is showing for all users rather than the specific users. So the desired outcome is not yet achieved.

While I can achieve the desired outcome using if-then logic in the view, it creates a lot of unnecessary duplication and encourages spaghettification. So I'm trying to do this in the controller.

Summary of Desired Outcome:

When the user loads the viewpage, the partial view should only render if the Windows Authenticated user belongs to one or more of a list of groups defined in the controller action. If the user is not authorized, then the partial view is not included.

Controller Block:

[ChildActionOnly]
    [Authorize(Roles="Domain\\GroupA,Domain\\GroupB")]
    public ActionResult MonitorCSU()
    {   
        return PartialView("MonitorCSU");            
    }

View Block:

<div class="rowWithCols3">
@Html.Partial("MonitorCSU")

Unsuccessful Iterations:

  • In the controller block I tried (unsuccessfully) to use an if-then block, the else case being another partial view with no content.

    [ChildActionOnly]
    public ActionResult MonitorCSU() { if (User.IsInRole("Domain\GroupA")) { return PartialView("_MonitorCSU"); } else { return PartialView("_Unauthorized"); } }

  • In Razor, I tried using HTML.Action but when I tried run the page the browser hung in an infinite loop.

  • My question is similar, I was doing this for my navigation. http://stackoverflow.com/a/17016292/1454538 - – matt. Jun 09 '15 at 22:20

3 Answers3

6

@Html.Partial() returns a partial view without calling a controller method. In order to call your controller method, you need to use

@Html.Action("MonitorCSU")

or

@{ Html.RenderAction("MonitorCSU") }

Note this assumes that the MonitorCSU() method is in the same controller as the method that generates the main view (other wise you also need to include a parameter for the controller name)

Refer documentation

  • Yes. This did the job.The next job is to figure out how to not prompt for login and simply assume the user is not in the correct authentication group, but that is a different topic. –  Jun 09 '15 at 22:01
2

While you've found a solution, you're going to have other problems with it. I would suggest a different approach, which is to use EditorTemplates and create a separate model for the html you want to render. Then, at runtime you would check whether the user is in the groups you specify, and if they are, you create an instance of the model, and if they are not you leave the model null. In this way, when the view is rendered with EditorFor(), it will ignore and not render the template for users who do not have access.

Erik Funkenbusch
  • 92,674
  • 28
  • 195
  • 291
  • 1
    Good idea. So "Editor Templates" is set of learning resources I look for? –  Jun 09 '15 at 22:30
  • 3
    @Rubix_Revenge - if you're not already using EditorTemplates, you're probably doing things the hard way. Just search for mvc and editortemplates and there are a number of good tutorials. Too much to go into in this answer. – Erik Funkenbusch Jun 09 '15 at 22:34
  • Understood. This is still a good lead, so I'll follow any skill improvement suggestions I see. –  Jun 09 '15 at 22:35
  • 1
    @Rubix_Revenge - one correction, I misspoke. You would need to create a list (or IEnumerable) of your special model, even if you are only rendering one of them. You would create an instance of the list, but you would only add your model to the list if the users were in the group. Hope that makes sense... a list of 0 items does not get rendered, but a null model would. – Erik Funkenbusch Jun 09 '15 at 22:55
  • In this case, the content of the partial view is strictly HTML that isn't bound to any model. Or maybe I'm not perceiving which what the model (i.e., class) is. The html content? –  Jun 10 '15 at 02:59
  • @Rubix_Revenge - If it's strictly html, then why do you need any MVC. Just set a flag in your pages model `bool accessToPrivate" or something... then in your controller, you check against the access groups and set that flag to true or false, in your view you just put a conditional if statement on the Html.Partial based on that Boolean. – Erik Funkenbusch Jun 10 '15 at 03:28
  • For this specific instance it's HTML. I didn't speak in generalities for the entire project. This is just one view requested by the customer and I was trying to find the cleanest way to do it. For the rest of the website I rely heavily on MVC. –  Jun 10 '15 at 05:11
  • The downside about StackOverflow is that my understanding is that you have to be to the point and stick with the facts, which makes it impossible to spell out the rest of the context. In this case, I can't really spell out the rest of the story. But maybe this exchanges are useful because it's gotten me to think about how I can move my permission logic into a database which then becomes a model. –  Jun 10 '15 at 16:20
1

Thanks to @Stephen Muecke and and a commenter whose entry has mysteriously vanished, I have the missing pieces.

I was able to test this code with several real users and verified the desired behavior happens consistently.

Controller Block: Main difference: take out authorization and use an if-then block send one of two partial views.

[ChildActionOnly]                
    public ActionResult MonitorCSU()
    {         
        if (User.IsInRole("DOMAIN\\GroupA")) 
        {
        return PartialView("MonitorCSU");         
        }
        else 
        {
        return PartialView("Unauthorized");
            // this is an empty page
        }
    }

View Block: The key difference is using HTML.Action

<div class="rowWithCols3">
@Html.Action("MonitorCSU")