Loupe

Razor Pages : conventions d’autorisation

Les Razors pages sont une nouveauté de ASP.NET Core 2.2. Elles permettent de se concentrer sur notre code de façon “page-focused” afin de nous rendre plus productif et plus rapide. Dans cet article nous verrons comment mettre en place une “convention d’autorisation” qui permettra d’appliquer une ou plusieurs policies à l’ensemble de pages de notre site web.

Convention d’autorisation : à quoi ça sert ?

Il est fastidieux mais aussi dangereux (car on peut en oublier) de mettre l’attribut [Authorize] sur chaque page de son site web.
Plus on écrit de code, plus on risque d’introduire un bug. Le plus simple pour diminuer le risque est d’avoir un seul mécanisme qui gère l’ensemble des autorisations de son site web.

Et comment ça fonctionne ?

La première étape consiste à créer une ou plusieurs “Policy” ce qui permettra de restreindre le niveau de droits de votre application.

Créer les policies

Pour notre exemple nous créerons 2 Policies fictives se basant sur les claims, cependant le reste de l’article reste aussi valable pour n’importe quel type de Policies (policy basé sur des Requirements etc…).

Création d’une classe de constante permettant d’avoir le noms des policies :

1
2
3
4
5
public sealed class Policies
{
        public const string IsSuperAdmin = "IsSuperAdmin";
        public const string IsAdmin = "IsAdmin ";
}

Dans la méthode ConfigureServices il faudra ajouter la méthode AddAuthorization comme ci-dessous.
Ajout des policies:

1
2
3
4
5
6
7
8
services.AddAuthorization(auth =>
{
   // vérification que le claim SuperAdmin existe et que sa valeur = True
   auth.AddPolicy(Policies.IsSuperAdmin, policy => policy.RequireClaim("SuperAdmin", "True"));
 
   // vérification que le claim Tenant existe
   auth.AddPolicy(Policies.IsTenant, policy => policy.RequireClaim("Tenant"));
});

Ajout des conventions :

Toujours dans la méthode ConfigureServices nous allons changer le comportement par défaut de la méthode AddMvc en appelant la méthode AddRazorPagesOptions.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
services.AddMvc()
     .AddRazorPagesOptions(options =>
     {
         // Toutes les pages dans le dossier SuperAdmin "devront" satisfaire la Policy IsSuperAdmin
         options.Conventions.AuthorizeFolder("/SuperAdmin", Policies.IsSuperAdmin);
         // Toutes les pages dans le dossier Tenant "devront" satisfaire la Policy IsTenant
         options.Conventions.AuthorizeFolder("/Tenant", Policies.IsTenant);
 
         // Toutes les pages dans le dossier Secured "devront" avoir un utilisateur connecté sans Policy particulière
         options.Conventions.AuthorizeFolder("/Secured");
 
         // Toutes les pages du dossier PublicPages ne requerront pas d'authentification
        options.Conventions.AllowAnonymousToFolder("/PublicPages");
     })
   .SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

Combiner les conventions

On pourra désactiver la sécurité sur une seule page de la manière suivante :

1
.AuthorizeFolder("/Admin", Policies.IsAdmin).AllowAnonymousToPage("/Admin/Public")

Cependant l’inverse ne fonctionnera pas :

1
.AllowAnonymousToFolder("/Public").AuthorizePage("/Public/Tenant",Policies.IsTenant)

De même, cette convention ne fonctionnera pas car elle vérifiera toujours que l’utilisateur est Tenant avant de vérifier si celui ci est SuperAdmin.

1
.AuthorizeFolder("/Tenant",Policies.IsTenant).AuthorizeFolder("/Admin/SA",, Policies.IsSuperAdmin)

Cependant ce genre de comportement peut être souhaitable, pour contourner cette limitation nous allons écrire notre propre convention et non utiliser les méthode “AuthorizeFolder” et “AllowAnonymous” :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
options.Conventions.AddFolderApplicationModelConvention("/", model =>
{
    string policy = null;
    // Si la route commence par "Tenant/SA" on choisit d'utiliser la Policy.IsSuperAdmin
    if (model.RouteTemplate.StartsWith("Tenant/SA"))
    {
        policy = Policies.IsSuperAdmin;
    }
    else if (model.RouteTemplate.StartsWith("Tenant"))
    {
       policy = Policies.IsTenant;
    }
    if (policy != null)
    {
         model.Filters.Add(new AuthorizeFilter(policy));
    }
});

Et voilà le tour est joué. En quelques lignes de codes nous avons pu appliquer l’ensemble des règles de sécurité sur tout notre site Web.

Happy coding

Photo de profil

Ces billets pourraient aussi vous intéresser

Vous nous direz ?!

Commentaires

comments powered by Disqus