2011-05-05 15 views
6

Uso @RenderSection("Contextual", false) dentro de mi _Layout.cshtml para permitir que diferentes vistas muestren su contenido particular allí. Algunos no tienen ninguno, otros lo hacen.Ocultar Html.ActionLinks basado en seguridad basada en roles

Además, uso la seguridad basada en roles y un ActionFilter para controlar si un usuario en particular tiene acceso a acciones de controlador particulares y, por lo tanto, rutas en mi sitio.

Lo que me gustaría hacer es proporcionar una sección @RenderSection("Contextual", false) en mi _Layout.cshtml y luego tener la página en particular proporcionar cualquier material contextual tiene sentido para esa página y tener el controlador correspondiente manejar la investigación de antecedentes de si un usuario puede realizar una acción y tal vez incluso ver que existen las opciones pero no estoy seguro de estar pensando en esto correctamente. Así es como son las cosas actualmente:

En este momento yo tengo una sección en una de mis archivos Index.cshtml así:

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

y luego en mi controlador correspondiente, tengo algo así como lo que :

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

Esto funciona como yo quiero (no-editores no tendrán la oportunidad de crear nuevos elementos) pero el crear entrada está allí para que todos lo vean. Puedo hacer algo así:

@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> 
} 

y que funciona bastante bien, ocultando la Crear un enlace de los no-Editores, pero estoy en la valla acerca de si es bueno o no para manejar de esta manera además de que Puedo ver que en el futuro tengo la situación en la que las reglas cambian y luego tengo dos ubicaciones para mantener sincronizadas: el atributo en la acción del controlador y el código en la vista.

¿Es este un enfoque razonable? ¿Hay una mejor manera de abordar esto?

Respuesta

8

Me gusta utilizar los indicadores que son más explícitos para el modelo de vista que se rellenan en el contorller.

Por ejemplo:

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

Por lo tanto, sería necesario añadir esta bandera a su modelo de vista o, posiblemente, en la clase base de sus modelos de vista. Puede ir por la ruta de crear un Custom Action Filter para completarlo en varios controladores o realizar algún control en la clase base de su controlador.

También me gusta definir un método útil extensión:

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

En función de la API que está utilizando, puede que también necesite extender MvcHtmlString.

A continuación, en la vista:

@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> 
} 

Usted puede decidir lo que le gustaría hacer con el div, es posible que desee tener otro ayudante que envuelve enlaces en divs, o tal vez se puede utilizar CSS para lograr cualquier diseño visual que estés buscando.

+0

Gracias por la respuesta. Supongo que lo que me gusta de tu solución es la idea de que la verificación de roles del usuario se realiza en el controlador y la Vista realmente no entra en el negocio de preocuparse por * por qué * la funcionalidad Crear está habilitada/deshabilitada, separa eso información fuera Permite cualquier cantidad de razones en el lado del controlador que se utilizarán para voltear el bit CanCreate. Buen material. Definitivamente * me gusta ese método de extensión, es * útil *. :) – itsmatt

+0

@itsmatt no hay problema. El sistema actual en el que estoy trabajando está lleno de (basado en el contexto + función) de seguridad, así que corre a través de esto mucho. Creo que tocas el punto clave, separando las preocupaciones de la vista del controlador. Otra ruta podría ser generar la lista de enlaces en función del rol (también podría estar poblado en el controlador) – TJB

0

Me gusta @ TJB respondo mucho, y creo que haré algo similar.Pero si quieres ir por una ruta diferente ... podrías crear tus propias LinkExtensions que sobrecarguen las LinkExtensions estándar.

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's disabled and not a link? 
      // Maybe do nothing?   
     } 
    } 
} 

Supongamos que "YourAccessStuff" está realmente implementado. Esto centralizaría esos controles de acceso en lugar de pegarlos en cada ActionLink. La desventaja, obviamente, es que aún puede olvidarse de poner su control de seguridad. Usar algún tipo de inyección de dependencia también lo haría más agradable.

Cuestiones relacionadas