En otras palabras, ¿es esta una idea realmente estúpida?¿Cómo creo un AuthorizeAttribute personalizado que sea específico para el área, el controlador y la acción?
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public class AuthorizeActionAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
// get the area, controller and action
var area = filterContext.RouteData.Values["area"];
var controller = filterContext.RouteData.Values["controller"];
var action = filterContext.RouteData.Values["action"];
string verb = filterContext.HttpContext.Request.HttpMethod;
// these values combined are our roleName
string roleName = String.Format("{0}/{1}/{2}/{3}", area, controller, action, verb);
// set role name to area/controller/action name
this.Roles = roleName;
base.OnAuthorization(filterContext);
}
}
ACTUALIZACIÓN que estoy tratando de evitar lo siguiente, en un escenario en el que tenemos muy granulares permisos de función debido a que los papeles son de configuración en función de cada cliente y se unen a grupos de usuarios:
public partial class HomeController : Controller
{
[Authorize(Roles = "/supplierarea/homecontroller/indexaction/")]
public virtual ActionResult Index()
{
return View();
}
[Authorize(Roles = "/supplierarea/homecontroller/aboutaction/")]
public virtual ActionResult About()
{
return View();
}
}
¿Alguien me puede aclarar de una manera segura para escribir este AuthorizeRouteAttribute para acceder a la información de la ruta y usar esto como el nombre de la función? Como dice Levi, el RouteData.Values no es seguro.
¿El uso de la ejecución de httpContext.Request.Path es una práctica más segura o mejor?
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (!filterContext.HttpContext.User.Identity.IsAuthenticated)
{
// auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
return;
}
var path = filterContext.HttpContext.Request.Path;
var verb = filterContext.HttpContext.Request.HttpMethod;
// these values combined are our roleName
string roleName = String.Format("{0}/{1}", path, verb);
if (!filterContext.HttpContext.User.IsInRole(roleName))
{
// role auth failed, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
// P.S. I want to tell the logged in user they don't
// have access, not ask them to login. They are already
// logged in!
return;
}
//
base.OnAuthorization(filterContext);
}
Esto tal vez ilustra el problema un poco más lejos:
enum Version
{
PathBasedRole,
InsecureButWorks,
SecureButMissingAreaName
}
string GetRoleName(AuthorizationContext filterContext, Version version)
{
//
var path = filterContext.HttpContext.Request.Path;
var verb = filterContext.HttpContext.Request.HttpMethod;
// recommended way to access controller and action names
var controller =
filterContext.ActionDescriptor.ControllerDescriptor.ControllerName;
var action =
filterContext.ActionDescriptor.ActionName;
var area = "oh dear...."; // mmmm, where's thearea name???
//
var insecureArea = filterContext.RouteData.Values["area"];
var insecureController = filterContext.RouteData.Values["controller"];
var insecureAction = filterContext.RouteData.Values["action"];
string pathRoleName =
String.Format("{0}/{1}", path, verb);
string insecureRoleName =
String.Format("{0}/{1}/{2}/{3}",
insecureArea,
insecureController,
insecureAction,
verb);
string secureRoleName =
String.Format("{0}/{1}/{2}/{3}",
area,
controller,
action,
verb);
string roleName = String.Empty;
switch (version)
{
case Version.InsecureButWorks:
roleName = insecureRoleName;
break;
case Version.PathBasedRole:
roleName = pathRoleName;
break;
case Version.SecureButMissingAreaName:
// let's hope they don't choose this, because
// I have no idea what the area name is
roleName = secureRoleName;
break;
default:
roleName = String.Empty;
break;
}
return roleName;
}
¿Podría agregar a su respuesta para mostrar cómo funciona su sugerencia en el código? Estamos usando áreas, por lo que tendría que reflejar esto, así como los controladores y las acciones. Fuera de interés, ¿realmente puede emparejar parcialmente con un controlador o acción (es decir, como se sugiere:/myarea/mycontroller/myaction '; Miembros de DROP TABLE; - /)? ¿Seguramente MVC no coincidirá con el controlador o la acción en primer lugar? – Junto
Respuesta actualizada para responder a su pregunta. – Levi
Hola Levi, entiendo que puedo usar (string controller = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName; string action = filterContext.ActionDescriptor.ActionName;) pero no puedo obtener acceso al nombre del área de la misma manera. Sin embargo, no hay AreaName disponible. ¿Dónde puedo ubicar eso? Un simple ejemplo de código cerraría esta pregunta. – Junto