policies net mvc example authorizationhandler asp asp.net-mvc linq-to-sql reflection attributes dynamic-linq

asp.net-mvc - net - authorize roles mvc 5



AutorizaciĆ³n de ASP.NET MVC basada en la pertenencia a un rol o la relaciĆ³n de datos(propiedad) (1)

Me gustaría extender AuthorizeAttribute en ASP.NET MVC para que admita el concepto de autorización de un usuario en función de su membresía de funciones O "propiedad" de los datos en cuestión. Estoy usando LINQ2SQL para acceso a datos. Hay una pregunta similar en la autorización de asp.net mvc utilizando roles .

Lo que estoy pensando es agregar los parámetros EntityProperty, UserProperty, RouteParameter y JoinTableType a mi clase AuthorizeAttribute ampliada. Los primeros dos serían los nombres de las propiedades en la tabla de unión para verificar. RouteParameter sería el nombre del parámetro de ruta que se extraerá para el valor de EntityProperty para que coincida. Obtendría el ID de usuario de la tabla del usuario usando el nombre de usuario actual. El parámetro JoinTableType sería el tipo de la tabla en el contexto de datos que contiene la entidad y las propiedades de usuario que el valor del parámetro de ruta y el ID de usuario deben coincidir.

La idea básica es, en pseudocódigo:

if authorizecore result is true user is granted access based on role else if user is not authenticated redirect to logon else if user is related to request user is granted access based on relation else user is not authorized, redirect to not authorized error view

La prueba relacionada es la siguiente:

result = false find the matching user from user name find the entity property value in route data if user exists and entity property value exists get table from context matching join table type if table exists find row in table matching user id and entity property value if row exists result = true endif endif endif return result

Mi pregunta es ¿cómo uso el tipo y los nombres de propiedad al construir la consulta LINQ? O voy a tener que hacer todo esto con object y reflexión. Realmente estoy buscando ideas sobre cómo hacer esto más fácil para que otras sugerencias también sean apreciadas. Prefiero usar el atributo en lugar de insertar la verificación directamente en la acción para mantener esto coherente con la forma en que manejo mis otras acciones.


Pude usar las extensiones Dynamic Linq de las muestras VS2008 para hacer esto de una manera bastante razonable. Aquí está el código que representa la segunda muestra de pseudocódigo de arriba. Pasa mi prueba unitaria inicial, pero tendré que hacerlo más robusto.

Uso:

[RoleOrMemberAuthorization( UserTable = "Participants", UserNameProperty = "UserName", UserSelectionProperty = "ParticipantID", JoinTable = "GroupLeaders", EntityProperty = "GroupID", UserEntityProperty = "ParticipantID", RouteParameter = "id", Roles = "SuperUser, ViewGroups" )]

Llamado:

else if (IsRelated( filterContext, this.GetTable( dc, this.JoinTable ), this.GetTable( dc, this.UserTable ) )) { SetCachePolicy( filterContext ); }

Fuente relevante:

protected bool IsRelated( AuthorizationContext filterContext, IQueryable joinTable, IQueryable userTable ) { bool result = false; try { object entityIdentifier = filterContext.RouteData .Values[this.RouteParameter]; object userIdentifier = this.GetUserIdentifer( filterContext, userTable ); if (userIdentifier != null && entityIdentifier != null) { result = joinTable.Where( this.EntityProperty + "=@0 and " + this.UserEntityProperty + "=@1", entityIdentifier, userIdentifier ) .Count() > 0; } } catch (NullReferenceException) { } return result; } private object GetUserIdentifer( AuthorizationContext filterContext, IQueryable userTable ) { string userName = filterContext.HttpContext.User.Identity.Name; var query = userTable.Where( this.UserNameProperty + "=@0", userName ) .Select( this.UserSelectionProperty ); object userIdentifer = null; foreach (var value in query) { userIdentifer = value; break; } return userIdentifer; } private IQueryable GetTable( DataContext context, string name ) { PropertyInfo info = context.GetType().GetProperty( name ); if (info != null) { return info.GetValue( context, null ) as IQueryable; } else { return null; } }