<style> .reveal { font-size: 27px; } mark { background-color: rgba(50, 168, 82, .5) !important; } </style> # I-CH 183 Implémenter la sécurité d’une application 2&#46; Authorisation © Selmir Hajruli & EPSIC 2022 --- ## Introduction - Role-based authorization - Claims-based authorization - Policy-based authorization - Resource-based authorization --- ## Role-based authorization * Lorsqu'une identité est créée, elle peut appartenir à un ou plusieurs rôles. Par exemple, Tracy peut appartenir aux rôles Administrateur et Utilisateur, tandis que Scott peut n'appartenir qu'au rôle Utilisateur. * La manière dont ces rôles sont créés et gérés dépend du processus d'autorisation de l'application. * Un point important à noter, sur la base des règles d'application, un seul utilisateur peut avoir plusieurs rôles. --- ## Roles in ASP.NET Core * Les contrôles d'autorisation basés sur les rôles sont déclaratifs - le développeur les intègre dans son code, sur un contrôleur ou une action dans un contrôleur, en spécifiant les rôles dont l'utilisateur actuel doit être membre pour accéder à la ressource demandée. * Par exemple, le code suivant limite l'accès à toutes les actions du `AdministrationController` aux utilisateurs qui sont membres du rôle d'administrateur : ```csharp [Authorize(Roles = "Administrator")] public class AdministrationController : Controller { } ``` --- ## Créer un rôle dans ASP.NET Core ```csharp public static class SeedDataApplicationRoles { public static void SeedRoles(RoleManager<IdentityRole> roleManager) { foreach (var role in new string[2] { "Admin", "User" }) { var result = roleManager.RoleExistsAsync(role).Result; if (!result) { roleManager.CreateAsync(new IdentityRole(role)); } } } } ``` --- ## Claim-based authorization * Lorsqu'une identité est créée, elle peut se voir attribuer un ou plusieurs claims. Un claim est une paire clé-valeur qui représente ce que le sujet est, et non ce qu'il peut faire. * Par exemple, vous pouvez avoir un permis de conduire, délivré par une autorité locale chargée des permis de conduire. Votre date de naissance figure sur votre permis de conduire. Dans ce cas, la clé serait `DateOfBirth`, la valeur serait votre date de naissance et et l'émetteur serait l'autorité chargée du permis de conduire. * L'autorisation basée sur les revendications, dans sa forme la plus simple, vérifie la valeur d'un claim et autorise l'accès à une ressource en fonction de cette valeur. * Par exemple, si vous voulez accéder à une boîte de nuit, le processus d'autorisation pourrait être le suivant * Le sécu évaluera la valeur de votre date de naissance et la confiance qu'il accorde à l'émetteur (l'autorité chargée du permis de conduire) avant de vous accorder l'accès. * Une identité peut contenir plusieurs claims avec plusieurs valeurs et peut contenir plusieurs claims du même type. --- ## Claims in ASP.NET Core * Sur un contrôleur ou une action on spécifie les claims que l'utilisateur actuel doit posséder, et éventuellement la valeur que le claim doit avoir pour accéder à la ressource demandée. * Les requirements en matière de claims sont basées sur une policy, on doit définir une policy exprimant ces requirements. * Cela se fait dans le cadre de la configuration du service d'autorisation, qui se fait normalement dans `ConfigureServices()` dans votre fichier Startup.cs. ```csharp services.AddAuthorization(options => { options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("EmployeeNumber")); options.AddPolicy("FoundersOnly", policy => policy.RequireClaim("EmployeeNumber", "1", "2")); }); ``` ```csharp [Authorize(Policy = "EmployeeOnly")] public class EmployeeController : Controller ``` ```csharp [Authorize(Policy = "FoundersOnly")] public class AdministrationController : Controller ``` --- ## Policy-based authorization - Sous le capot, l'autorisation basée sur les rôles et l'autorisation basée sur les claims utilisent un requirement, un gestionnaire de requirements et une policy de base. - Ces éléments de base permettent d'exprimer les évaluations d'autorisation dans le code. - Le résultat est une structure d'autorisation plus riche, réutilisable et testable. - Une policy d'autorisation est constituée d'un ou plusieurs requirements. Elle est enregistrée dans le cadre de la configuration du service d'autorisation. --- ## Policies in ASP.NET Core * Comme on l'a fait dans le cadre de claims, on doit définir une policy exprimant les requirements. * Cela se fait dans le cadre de la configuration du service d'autorisation, qui se fait normalement dans `ConfigureServices()` dans votre fichier Startup.cs. ```csharp public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.AddRazorPages(); services.AddAuthorization(options => { options.AddPolicy("AtLeast18", policy => policy.Requirements.Add(new MinimumAgeRequirement(18))); }); } }); ``` * Dans cet exemple, une policy "AtLeast18" est créée. Elle n'a qu'un seul requirement, celle d'un âge minimum, qui est fourni comme paramètre du requirement. --- ## Policies : Requirements * Un requirement d'autorisation est un ensemble de paramètres de données qu'une policy peut utiliser pour évaluer l'utilisateur actuel. Dans notre policy "AtLeast18", le requirement a un seul paramètre, l'âge minimum. ```csharp public class MinimumAgeRequirement : IAuthorizationRequirement { public int MinimumAge { get; } public MinimumAgeRequirement(int minimumAge) { MinimumAge = minimumAge; } } ``` :::info Si une policy d'autorisation contient plusieurs requirements d'autorisation, ils doivent tous être satisfaits pour que l'évaluation de la policy réussisse. En d'autres termes, les requirements d'autorisation multiples ajoutées à une policy d'autorisation unique sont traitées avec des opérateurs ==AND==. ::: --- ## Policies : Authorization handlers * Un authorization handler est responsable de l'évaluation des propriétés d'un requirement. Il évalue les requirements par rapport à un `AuthorizationHandlerContext` fourni pour déterminer si l'accès est autorisé ou non. ```csharp public class MinimumAgeHandler : AuthorizationHandler<MinimumAgeRequirement> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, MinimumAgeRequirement requirement) { if (!context.User.HasClaim(c => c.Type == ClaimTypes.DateOfBirth)) return Task.CompletedTask; var dateOfBirth = Convert.ToDateTime(context.User.FindFirst(c => c.Type == ClaimTypes.DateOfBirth); int calculatedAge = DateTime.Today.Year - dateOfBirth.Year; if (calculatedAge >= requirement.MinimumAge) context.Succeed(requirement); return Task.CompletedTask; } } ``` ```csharp services.AddSingleton<IAuthorizationHandler, MinimumAgeHandler>(); ``` --- ## Resource-based authorization * Cette stratégie d'autorisation dépend de la ressource à laquelle on accède. * Considérons un document qui a une propriété `AuthorId`. * Seul l'auteur est autorisé à mettre à jour le document. * Par conséquent, le document doit être récupéré dans base de données avant que l'évaluation de l'autorisation puisse avoir lieu. --- ## Resources in ASP.NET Core * Le check pour les autorisation basées sur les ressources est un peu plus manuel, il faut injecter le `IAuthorizationService` dans notre contrôleur : ```csharp public class DocumentsController : Controller { private readonly IAuthorizationService _authorizationService; private readonly IDocumentsService _documentsService; public DocumentsController(IAuthorizationService authorizationService, IDocumentsService documentsService) { _authorizationService = authorizationService; _documentsService = documentsService; } } ``` --- ## Resources : check authorization * Puis faire manuellement le check `_authorizationService.AuthorizeAsync` dans la méthode notre contrôleur. ```csharp public async Task<IActionResult> Get(Guid documentId) { var doc = _documentsService.Find(documentId); if (doc == null) return new NotFound(); var authorizationResult = await _authorizationService.AuthorizeAsync(User, doc, "DocumentsPolicy"); if (authorizationResult.Succeeded) return Ok(doc); else return Forbid(); } ``` --- ## Resources : handler + requirement * Un handler pour l'autorisation basée sur les ressources n'est pas très différent de celui pour les policies. ```csharp public class DocumentAuthorizationHandler : AuthorizationHandler<SameAuthorRequirement, Document> { protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, SameAuthorRequirement requirement, Document resource) { var userId = context.User.FindFirst("Id"); if (userId.Value == resource.User.Id) context.Succeed(requirement); return Task.CompletedTask; } } public class SameAuthorRequirement : IAuthorizationRequirement { } ``` --- ## Exercices :::info - git reset --hard - git fetch - git pull ::: 1. Seul un user ayant le rôle admin peut créer des autres users et définir s'ils sont "admin" ou "user". 1. Un user doit avoir un claim `IsMedecin` qui indique `true` s'il est. * Se baser sur ce claim pour autoriser la création de `TestCovid`, seul un médecin peut créer un test, même un admin non-médecin ne peut pas. 1. Un user "lambda" peut venir consulter les stats COVID (ressource publique) et consulter le résultat de ses tests COVID mais pas ceux des autres patients. 1. Pour finir, un user ayant une adresse email en `@chuv.ch` peut supprimer les tests covid. :::spoiler 1. Roles, 2. Claims, 3. Ressources, 4. Policy :::
{"metaMigratedAt":"2023-06-15T21:43:58.080Z","metaMigratedFrom":"YAML","title":"I-CH 183 - 2. Authorisation","breaks":true,"slideOptions":"{\"theme\":\"moonl\",\"spotlight\":{\"enabled\":false}}","contributors":"[{\"id\":\"2ff8bf3a-d09c-4308-a7a7-64e5fb1c4783\",\"add\":15227,\"del\":5012}]"}
    192 views