Autoriser l'attribut avec plusieurs rôles

97

Je voudrais ajouter une autorisation à un contrôleur, pour plusieurs rôles à la fois.

Normalement, cela ressemblerait à ceci:

[Authorize(Roles = "RoleA,RoleB,RoleC")]
public async Task<ActionResult> Index()
{
}

Mais j'ai stocké mes rôles dans consts, car ils peuvent changer ou être étendus à un moment donné.

public const RoleA = "RoleA";
public const RoleB = "RoleB";
public const RoleC = "RoleC";

Je ne peux pas faire cela, car la chaîne doit être connue au moment de la compilation:

[Authorize(Roles = string.join(",",RoleA,RoleB,RoleC)]
public async Task<ActionResult> Index()
{
}

Existe-t-il un moyen de contourner le problème?

JE POURRAIS écrire un const qui contient simplement "RoleA, RoleB, RoleC" - mais je n'aime pas les chaînes magiques et c'est une chaîne magique. Changer le nom d'un rôle et oublier de changer la chaîne combinée serait un désastre.

J'utilise MVC5. L'identité ASP.NET et le rôle sont connus au moment de la compilation.

Christian Sauer
la source
utilisez-vous la chaîne publique const RoleA = "RoleA"; ou comme vous l'avez écrit en question?
Mukesh Modhvadiya

Réponses:

188

Essayez de créer un attribut d'autorisation personnalisé comme celui-ci .

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = string.Join(",", roles);
    }
}

En supposant que vos rôles seront les mêmes pour plusieurs contrôleurs, créez une classe d'assistance:

public static class Role
{
    public const string Administrator = "Administrator";
    public const string Assistant = "Assistant";
}

Ensuite, utilisez-le comme ceci:

public class MyController : Controller
{
    [AuthorizeRoles(Role.Administrator, Role.Assistant)]
    public ActionResult AdminOrAssistant()
    {                       
        return View();
    }
}
MacGyver
la source
12
Voilà une idée digne de Mac Gyver;)
Christian Sauer
2
Très belle solution :)
aup
1
J'aime aussi beaucoup cette solution, surtout parce que je peux laisser mon rôle être une énumération plutôt qu'une chaîne. Quels seraient un bon espace de noms et un bon emplacement dans la hiérarchie du projet pour placer cet attribut d'autorisation personnalisé?
Simon Shine
4
Je ne suis pas sûr de ce qui se passe ici, mais cela ne m'a pas aidé, n'importe quel utilisateur quel que soit le rôle pouvait accéder à la méthode.
Urielzen
2
Même problème que @Urielzen, mais il a été résolu par la réponse ci-dessous de Jerry Finegan (en utilisant "System.Web.Mvc.AuthorizeAttribute et NOT System.Web.Http.AuthorizeAttribute")
RJB
13

Assurez-vous que vous dérivez votre classe d'attributs personnalisée désactivée System.Web.Mvc.AuthorizeAttributeet NON System.Web.Http.AuthorizeAttribute.

J'ai rencontré le même problème. Une fois que je l'ai changé, tout a fonctionné.

Vous pouvez également ajouter les éléments suivants à votre classe d'attributs personnalisés:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class, Inherited = true, AllowMultiple = true)] 
Jerry Finegan
la source
Je viens d'essayer et j'ai trouvé en faisant référence à la bibliothèque au System.Web.Http.AuthorizeAttributelieu deSystem.Web.Mvc.AuthorizeAttribute
fraser jordan
10

Le moyen le meilleur et le plus simple que j'ai trouvé pour résoudre ce problème est simplement de concaténer les rôles dans l'attribut Authorize.

[Authorize(Roles = CustomRoles.Admin + "," + CustomRoles.OtherRole)]

avec CustomRole une classe avec des chaînes constantes comme celle-ci:

public static class CustomRoles
{
    public const string Admin = "Admin";
    // and so on..
}
ChristopheHvd
la source
2
Précieux; mais cela devrait être un commentaire; pas une réponse.
GhostCat
1
Solution simple et élégante!
Iosif Bancioiu
Votre réponse et la réponse acceptée déclencheront l'autorisation si elles sont correctement mises en œuvre (j'utilise l'accepté dans une application Web de production). Proposer une modification pour supprimer les commentaires sur la réponse acceptée.
Eric Eskildsen
3

Ce que j'ai fait est la réponse dans @Tieson

Je peaufine un peu sa réponse. Au lieu d'une chaîne, pourquoi ne pas la convertir en liste?

Voici ma réponse:

public class AuthorizeRolesAttribute : AuthorizeAttribute
{
    private new List<string> Roles;
    public AuthorizeRolesAttribute(params string[] roles) : base()
    {
        Roles = roles.toList()
    }
}

Et puis vérifiez si le rôle est valide en remplaçant OnAuthorization

public override void OnAuthorization(HttpActionContext actionContext)
{
            if (Roles == null)
                HandleUnauthorizedRequest(actionContext);
            else
            {
                ClaimsIdentity claimsIdentity = HttpContext.Current.User.Identity as ClaimsIdentity;
                string _role = claimsIdentity.FindFirst(ClaimTypes.Role).Value;
                bool isAuthorize = Roles.Any(role => role == _role);

                if(!isAuthorize)
                    HandleUnauthorizedRequest(actionContext);
            }
        }

Et là vous l'avez, il valide maintenant si le rôle est autorisé à accéder à la ressource

Christopher Enriquez
la source
1

J'ai l'impression qu'un attribut d'autorisation personnalisé est excessif pour ce problème, sauf si vous avez un grand nombre de rôles.

Étant donné que la chaîne doit être connue au moment de la compilation, pourquoi ne pas créer une classe de rôle statique contenant des chaînes publiques des rôles que vous avez définis, puis ajouter des chaînes séparées par des virgules avec certains rôles que vous souhaitez autoriser:

public static class Roles
{
    public const string ADMIN = "Admin";
    public const string VIEWER = "Viewer";

    public const string ADMIN_OR_VIEWER = ADMIN + "," + VIEWER;
}

Et puis vous pouvez utiliser l'attribut Authorize comme ceci sur la classe de contrôleur ou la méthode de contrôleur (ou les deux):

[Authorize(Roles = Roles.ADMIN]
public class ExampleController : Controller
{
    [Authorize(Roles = Roles.ADMIN_OR_VIEWER)
    public ActionResult Create()
    {
        ..code here...
    }
}
Robert Tuttle
la source
1
Cet exemple ne fonctionne pas, ou du moins pas comme vous le pensez. Par exemple, alors que le nouveau ADMIN_OR_VIEWERrôle sur l'action est redondant, car vous ne serez pas autorisé à accéder à la Createméthode si vous ne possédez pas déjà le ADMINrôle. Dans ce cas VIEWER, vous ne pourrez jamais appeler la Createméthode.
John Leidegren
Cette solution n'est pas non plus évolutive. Il y aura un moment où vous aurez trop de rôles avec des actions différentes et vous ne devriez pas créer chaque combinaison
EduLopez