2009-07-18 20 views
27

Tengo un controlador y quiero dos roles para poder acceder a él. 1-admin OR 2-moderatorasp.net mvc decorate [Autorizar()] con varias enumeraciones

Sé que puedes hacer [Authorize (Roles = "admin, moderators")] pero tengo mis roles en una enumeración. Con la enumeración, solo puedo autorizar UNA función. No puedo entender cómo autorizar dos.

He intentado algo como [Authorize (Roles = MyEnum.Admin, MyEnum.Moderator)] pero eso no se compilará.

Alguien sugirió una vez esto:

[Authorize(Roles=MyEnum.Admin)] 
[Authorize(MyEnum.Moderator)] 
public ActionResult myAction() 
{ 
} 

pero no funciona como un quirófano. Creo que en este caso el usuario debe ser parte de AMBOS roles. ¿Estoy pasando por alto alguna sintaxis? ¿O es este un caso en el que tengo que rodar mi propia autorización personalizada?

+5

Interesante. ¿Cómo fue capaz de hacer que esto funcione incluso con un solo rol en una enumeración? ¿MyEnum.Admin devuelve una cadena? Estoy tratando de hacer lo mismo que tú, y me he encontrado con un par de problemas: - No puedo configurar una enumeración de tipo de cadena. - No puedo llamar a ToString() en la enumeración, p. [Autorizar (Roles = MyEnum.Admin.ToString())] Ambos ejemplos anteriores me dan errores de compilación. Si puede aconsejar cómo hizo que esto funcione, eso sería apreciado. Gracias. –

+0

@JohnnyO - Tengo el mismo problema, ¿lograron descubrir lo que estamos haciendo mal? @codette - ¿podría darnos un consejo aquí? – UpTheCreek

+0

Lo siento pero nunca fui con ninguna de estas soluciones. Así que cambié mi código para que solo se verifique una función. Cuanto mayor sea su función, más podrán hacer. Por ejemplo, un usuario "normal" puede hacer algunas cosas. Un "moderador" puede hacer todo lo que un usuario "normal" puede hacer y más. Un "administrador" puede hacer todo lo que un usuario "normal" y "moderador" pueden y más. – codette

Respuesta

29

intente utilizar el operador OR bit de la siguiente manera:

[Authorize(Roles= MyEnum.Admin | MyEnum.Moderator)] 
public ActionResult myAction() 
{ 
} 

Si eso no funciona, usted podría rodar su propia. Actualmente solo hice esto en mi proyecto. Esto es lo que hice:

public class AuthWhereRole : AuthorizeAttribute 
{ 
    /// <summary> 
    /// Add the allowed roles to this property. 
    /// </summary> 
    public UserRole Is; 

    /// <summary> 
    /// Checks to see if the user is authenticated and has the 
    /// correct role to access a particular view. 
    /// </summary> 
    /// <param name="httpContext"></param> 
    /// <returns></returns> 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     if (httpContext == null) 
      throw new ArgumentNullException("httpContext"); 

     // Make sure the user is authenticated. 
     if (!httpContext.User.Identity.IsAuthenticated) 
      return false; 

     UserRole role = someUser.Role; // Load the user's role here 

     // Perform a bitwise operation to see if the user's role 
     // is in the passed in role values. 
     if (Is != 0 && ((Is & role) != role)) 
      return false; 

     return true; 
    } 
} 

// Example Use 
[AuthWhereRole(Is=MyEnum.Admin|MyEnum.Newbie)] 
public ActionResult Test() {} 

Además, asegúrese de añadir un atributo indicadores a su enumeración y asegurarse de que son apreciados más de 1 y hasta. De esta manera:

[Flags] 
public enum Roles 
{ 
    Admin = 1, 
    Moderator = 1 << 1, 
    Newbie = 1 << 2 
    etc... 
} 

El cambio de bit izquierdo da los valores 1, 2, 4, 8, 16 y así sucesivamente.

Bueno, espero que esto ayude un poco.

+0

¡Perfecto! ¡Justo lo que necesitaba! Gracias. – Kamyar

+2

¿Qué sucede si el usuario puede pertenecer a más de un rol? – ssmith

+0

¿Qué valor necesito tener en mi base de datos para poder seleccionar el administrador, el moderador o el novato? (db.users.role = 1; 2; 4; 8 ...? – Misi

-1

O usted podría concatenar como:

[autorice (Roles = Common.Lookup.Item.SecurityRole.Administrator + "" + Common.Lookup.Item.SecurityRole.Intake)]

0

Para añadir al código de CalebHC y responder la pregunta de ssmith sobre el manejo de usuarios que tienen múltiples roles ...

Nuestro principal de seguridad personalizada devuelve una matriz de cadenas que representa todos los grupos/roles en los que se encuentra un usuario. Primero, debemos convertir todas las cadenas en la matriz que combina elementos en la enumeración. Finalmente, buscamos cualquier coincidencia, si es así, entonces el usuario está autorizado.

Tenga en cuenta que también estamos redirigiendo a un usuario no autorizado a una vista personalizada "No autorizada".

La clase entera se ve así:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] 
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 
    /// <summary> 
    /// Add the allowed roles to this property. 
    /// </summary> 
    public Roles Is { get; set; } 

    /// <summary> 
    /// Checks to see if the user is authenticated and has the 
    /// correct role to access a particular view. 
    /// </summary> 
    /// <param name="httpContext"></param> 
    /// <returns></returns> 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     if (httpContext == null) 
      throw new ArgumentNullException("httpContext"); 

     if (!httpContext.User.Identity.IsAuthenticated) 
      return false; 

     var iCustomPrincipal = (ICustomPrincipal) httpContext.User; 

     var roles = iCustomPrincipal.CustomIdentity 
         .GetGroups() 
         .Select(s => Enum.Parse(typeof (Roles), s)) 
         .ToArray(); 

     if (Is != 0 && !roles.Cast<Roles>().Any(role => ((Is & role) == role))) 
     { 
      return false; 
     } 

     return true; 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
     if (filterContext == null) 
      throw new ArgumentNullException("filterContext"); 

     filterContext.Result = new ViewResult { ViewName = "NotAuthorized" }; 
    } 
} 
31

Aquí es una solución sencilla y elegante, que le permite utilizar simplemente la siguiente sintaxis:

[AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)] 

Al crear su propio atributo, utilice la palabra clave params en su constructor:

public class AuthorizeRoles : AuthorizeAttribute 
{ 
    public AuthorizeRoles(params MyEnum[] roles) 
    { 
     ... 
    } 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
     ... 
    } 
} 

Esto le permitirá utilizar el atributo de la siguiente manera:

[AuthorizeRoles(MyEnum.Admin, MyEnum.Moderator)] 
public ActionResult myAction() 
{ 
} 
+6

Creo que esta debería ser una solución aceptada, ya que funciona, y no está limitado a 32 roles como máximo en la respuesta aceptada. Gracias por esto. – Zoka

+0

Solo un indicio de lo que está sucediendo aquí (bueno, me engañó durante 5 minutos!). Está creando el objeto AuthorizeRoles y luego llamará a AuthorizeCore para averiguar si desea otorgar acceso o no. Algo así podría ayudar. http://stackoverflow.com/questions/1148312/ asp-net-mvc-decorate-authorize-with-multiple-enums –

+0

@zoka De acuerdo. Esta solución es mejor y más flexible que la mía :) – CalebHC

2

Trate

public class CustomAuthorize : AuthorizeAttribute 
{ 
    public enum Role 
    { 
     DomainName_My_Group_Name, 
     DomainName_My_Other_Group_Name 
    } 

    public CustomAuthorize(params Role[] DomainRoles) 
    { 
     foreach (var domainRole in DomainRoles) 
     { 
      var domain = domainRole.ToString().Split('_')[0] + "_"; 
      var role = domainRole.ToString().Replace(domain, "").Replace("_", " "); 
      domain=domain.Replace("_", "\\"); 
      Roles += ", " + domain + role; 
     } 
     Roles = Roles.Substring(2); 
    }  
} 

public class HomeController : Controller 
{ 
    [CustomAuthorize(Role.DomainName_My_Group_Name, Role.DomainName_My_Other_Group_Name)] 
    public ActionResult Index() 
    { 
     return View(); 
    } 
} 
1

Aquí está mi versión, basada en @CalebHC y respuestas de @Lee Harold.

He seguido el estilo del uso de parámetros con nombre en el atributo y he anulado la propiedad de las clases base Roles.

@ respuesta de CalebHC utiliza un nuevo Is propiedad que creo que es innecesario, porque se AuthorizeCore() anulados (que en la clase base utiliza roles) por lo que tiene sentido usar nuestra propia Roles también. Al usar nuestro propio Roles, escribimos Roles = Roles.Admin en el controlador, que sigue el estilo de otros atributos .Net.

He usado dos constructores a CustomAuthorizeAttribute para mostrar los nombres de grupos de directorios activos reales que se pasa en la producción En utilizo el constructor parametrizado para evitar cadenas mágicas en la clase:. Nombres de grupo se extraen de web.config durante Application_Start() y pasó en la creación usando una herramienta DI.

Necesitará un NotAuthorized.cshtml o similar en su carpeta Views\Shared o los usuarios no autorizados recibirán una pantalla de error.

Aquí está el código para la clase base AuthorizationAttribute.cs.

controlador:

public ActionResult Index() 
{ 
    return this.View(); 
} 

[CustomAuthorize(Roles = Roles.Admin)] 
public ActionResult About() 
{ 
    return this.View(); 
} 

CustomAuthorizeAttribute:

// The left bit shifting gives the values 1, 2, 4, 8, 16 and so on. 
[Flags] 
public enum Roles 
{ 
    Admin = 1, 
    User = 1 << 1  
} 

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] 
public class CustomAuthorizeAttribute : AuthorizeAttribute 
{ 
    private readonly string adminGroupName; 

    private readonly string userGroupName; 

    public CustomAuthorizeAttribute() : this("Domain Admins", "Domain Users") 
    {  
    } 

    private CustomAuthorizeAttribute(string adminGroupName, string userGroupName) 
    { 
    this.adminGroupName = adminGroupName; 
    this.userGroupName = userGroupName; 
    } 

    /// <summary> 
    /// Gets or sets the allowed roles. 
    /// </summary> 
    public new Roles Roles { get; set; } 

    /// <summary> 
    /// Checks to see if the user is authenticated and has the 
    /// correct role to access a particular view. 
    /// </summary> 
    /// <param name="httpContext">The HTTP context.</param> 
    /// <returns>[True] if the user is authenticated and has the correct role</returns> 
    /// <remarks> 
    /// This method must be thread-safe since it is called by the thread-safe OnCacheAuthorization() method. 
    /// </remarks> 
    protected override bool AuthorizeCore(HttpContextBase httpContext) 
    { 
    if (httpContext == null) 
    { 
     throw new ArgumentNullException("httpContext"); 
    } 

    if (!httpContext.User.Identity.IsAuthenticated) 
    { 
     return false; 
    } 

    var usersRoles = this.GetUsersRoles(httpContext.User); 

    return this.Roles == 0 || usersRoles.Any(role => (this.Roles & role) == role); 
    } 

    protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) 
    { 
    if (filterContext == null) 
    { 
     throw new ArgumentNullException("filterContext"); 
    } 

    filterContext.Result = new ViewResult { ViewName = "NotAuthorized" }; 
    } 

    private IEnumerable<Roles> GetUsersRoles(IPrincipal principal) 
    { 
    var roles = new List<Roles>(); 

    if (principal.IsInRole(this.adminGroupName)) 
    { 
     roles.Add(Roles.Admin); 
    } 

    if (principal.IsInRole(this.userGroupName)) 
    { 
     roles.Add(Roles.User); 
    } 

    return roles; 
    }  
} 
+0

En caso de que alguien realmente intente usar este atributo con la inyección del constructor, no lo hará trabajo porque los atributos son c reaccionado a través de la API de reflexión - vea esta [publicación] (http://alexmg.com/post/2012/09/01/New-features-in-the-Autofac-MVC-4-and-Web-API- (Beta)) -Integrations.aspx) para más información. El código publicado anteriormente fue un primer corte antes de hacerlo funcionar con DI. – meataxe

+0

Al final, decidí no utilizar DI con esto porque la lib que estoy usando (Autofac + Autofac.MVC4) solo es compatible con lo que intento lograr en una versión beta, que no quiero usar. Aquí hay un enlace sobre cómo hacer este tipo de cosas con [Autofac.MVC4 beta] (http://alexmg.com/post/2012/09/01/New-features-in-the-Autofac-MVC-4 -y-API web-% 28Beta% 29-Integrations.aspx). Mi versión final eliminó los argumentos del constructor, y convertí 'adminGroupName' y' userGroupName' en 'const'. – meataxe

3

que combinan algunas de las soluciones aquí para crear mi favorito personal. Mi atributo personalizado simplemente cambia los datos para que estén en el formato que espera SimpleMembership y le permite manejar todo lo demás.

Mis papeles enumeración:

public enum MyRoles 
{ 
    Admin, 
    User, 
} 

crear roles: atributos

public static void CreateDefaultRoles() 
{ 
    foreach (var role in Enum.GetNames(typeof(MyRoles))) 
    { 
     if (!Roles.RoleExists(role)) 
     { 
      Roles.CreateRole(role); 
     } 
    } 
} 

personalizado:

public class AuthorizeRolesAttribute : AuthorizeAttribute 
{ 
    public AuthorizeRolesAttribute(params MyRoles[] allowedRoles) 
    { 
     var allowedRolesAsStrings = allowedRoles.Select(x => Enum.GetName(typeof(MyRoles), x)); 
     Roles = string.Join(",", allowedRolesAsStrings); 
    } 
} 

Se utiliza este modo:

[AuthorizeRoles(MyRoles.Admin, MyRoles.User)] 
public ActionResult MyAction() 
{ 
    return View(); 
} 
Cuestiones relacionadas