ASP.NET MVC 4 Atributo de autorización personalizado con códigos de permiso (sin roles)

121

Necesito controlar el acceso a las vistas según los niveles de privilegios de los usuarios (no hay roles, solo niveles de privilegios para los niveles de operación CRUD asignados a los usuarios) en mi aplicación MVC 4.

Como ejemplo; debajo de AuthorizeUser será mi atributo personalizado y necesito usarlo así:

[AuthorizeUser(AccessLevels="Read Invoice, Update Invoice")]
public ActionResult UpdateInvoice(int invoiceId)
{
   // some code...
   return View();
}


[AuthorizeUser(AccessLevels="Create Invoice")]
public ActionResult CreateNewInvoice()
{
  // some code...
  return View();
}


[AuthorizeUser(AccessLevels="Delete Invoice")]
public ActionResult DeleteInvoice(int invoiceId)
{
  // some code...
  return View();
}

¿Es posible hacerlo de esta manera?

chatura
fuente

Respuestas:

243

Podría hacer esto con un atributo personalizado de la siguiente manera.

[AuthorizeUser(AccessLevel = "Create")]
public ActionResult CreateNewInvoice()
{
    //...
    return View();
}

Clase de atributo personalizado de la siguiente manera.

public class AuthorizeUserAttribute : AuthorizeAttribute
{
    // Custom property
    public string AccessLevel { get; set; }

    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var isAuthorized = base.AuthorizeCore(httpContext);
        if (!isAuthorized)
        {                
            return false;
        }

        string privilegeLevels = string.Join("", GetUserRights(httpContext.User.Identity.Name.ToString())); // Call another method to get rights of the user from DB

        return privilegeLevels.Contains(this.AccessLevel);           
    }
}

Puede redirigir a un usuario no autorizado en su costumbre AuthorisationAttributeanulando el HandleUnauthorizedRequestmétodo:

protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
    filterContext.Result = new RedirectToRouteResult(
                new RouteValueDictionary(
                    new
                        { 
                            controller = "Error", 
                            action = "Unauthorised" 
                        })
                );
}
chatura
fuente
Probé su ejemplo de HandleUnauthorizedRequest pero cuando especifico el RouteValueDictionary, simplemente me redirige a una ruta que no existe. Agrega la ruta a la que quiero redirigir al usuario a la ruta a la que el usuario quería acceder ... si obtengo algo como: localhost: 9999 / admin / Home cuando quería localhost: 9999 / Home
Marin
1
@Marin Intenta agregar area = string.Empty en el RouteValueDictionary
Alex
30
Estaba votando a favor, pero luego vi "if (condition) {return true;} else {return false;}" al final ...
GabrielBB
1
@Emil Simplemente devolvería el booleano que me dio el método String.Contains. Pero esto es irrelevante, no voté en contra, simplemente no voté a favor, jeje.
GabrielBB
2
.Name.ToString()es redundante, ya que la Namepropiedad ya es una cadena
FindOutIslamNow
13

Aquí hay una modificación de la anterior. responder. La principal diferencia es que cuando el usuario no está autenticado, utiliza el método original "HandleUnauthorizedRequest" para redirigir a la página de inicio de sesión:

   protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
    {

        if (filterContext.HttpContext.User.Identity.IsAuthenticated) {

            filterContext.Result = new RedirectToRouteResult(
                        new RouteValueDictionary(
                            new
                            {
                                controller = "Account",
                                action = "Unauthorised"
                            })
                        );
        }
        else
        {
             base.HandleUnauthorizedRequest(filterContext);
        }
    }
Leonid Minkov
fuente
3

Tal vez esto sea útil para cualquiera en el futuro, he implementado un atributo de autorización personalizado como este:

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

    public ClaimAuthorizeAttribute(string Claim)
    {
        _claim = Claim;
    }

    public void OnAuthorization(AuthorizationFilterContext context)
    {
        var user = context.HttpContext.User;
        if(user.Identity.IsAuthenticated && user.HasClaim(ClaimTypes.Name, _claim))
        {
            return;
        }

        context.Result = new ForbidResult();
    }
}
RafaelH
fuente
0

Si usa la API WEB con reclamos, puede usar esto:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class AutorizeCompanyAttribute:  AuthorizationFilterAttribute
{
    public string Company { get; set; }

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var claims = ((ClaimsIdentity)Thread.CurrentPrincipal.Identity);
        var claim = claims.Claims.Where(x => x.Type == "Company").FirstOrDefault();

        string privilegeLevels = string.Join("", claim.Value);        

        if (privilegeLevels.Contains(this.Company)==false)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Unauthorized, "Usuario de Empresa No Autorizado");
        }
    }
}
[HttpGet]
[AutorizeCompany(Company = "MyCompany")]
[Authorize(Roles ="SuperAdmin")]
public IEnumerable MyAction()
{....
}
Maurico Bello
fuente