Jusqu’à maintenant, nous avons géré notre site sans nous soucier d’un problème majeur : l’utilisateur doit-il être enregistré pour envoyer des postes ou des commentaires ?
C’est là qu’intervient cette partie. Nous vous guiderons pas à pas pour mettre en place une gestion complète des autorisations liées à chaque utilisateur. Les deux mots clef de cette partie sont authentification et autorisation.
Authentifier un utilisateur, le login et le mot de passe
Lorsque vous naviguez sur un site web, il est fort probable que vous tombiez sur un formulaire qui vous demande votre nom d’utilisateur (le login, qu’on peut traduire par identifiant en français) et votre mot de passe1.
Cette fonctionnalité est très simple à mettre en place : par défaut ASP.NET vous fournit toutes les bases pour créer les modèles associés à la fonctionnalité d’authentification par mot de passe.
Le saviez vous? Le mot "authentification" se distingue du mot "identification" par le fait qu’une authentification assure que la personne est bien celle qu’elle prétend être. À l’opposé, une simple identification permet à la personne de déclarer qui elle est. C’est pour ça que vous avez un "identifiant" et un "mot de passe". Le premier vous permet de déclarer votre identité, le second, comme il n’est connu que de vous (même pas de la plateforme) permet de vous authentifier.
Lorsque vous avez sélectionné le type de projet (…) Visual Studio a immédiatement créé un modèle, un contrôleur et quelques vues.
Je vous laisserez décortiquer les vues mais il est important de comprendre comment fonctionne le contrôleur et le modèle de données.
# Le contrôleur
Le modèle de code Visual Studio a généré un ensemble de méthodes qui sont là pour implémenter de manière automatique toutes les fonctionnalités habituelles d’une authentification par login et mot de passe.
- l’inscription, qui se dit register en anglais,
- l’authentification en elle-même,
- la possibilité de récupérer son mot de passe en cas de perte,
- la désinscription,
- et nous le verrons en fin de chapitre : l’utilisation de fournisseurs externes tels que google, twitter… pour se connecter.
Le contrôleur se trouve dans Controllers\AccountController.cs
. Il contient un certains nombre de méthodes simples (return View();
) qu’il vous appartiendra de modifier si cela vous semble nécessaire : elles ne font qu’afficher la page qui contient le formulaire.
Les autres sont un peu plus complexes pour deux raisons :
- elles utilisent à volonté le principe des méthodes asynchrones ;
- elles se basent sur le composant
Microsoft.AspNet.Identity.Owin
, qui est le composant maître de ce chapitre.
Les versions les plus récentes d’ASP.NET ont introduit le composant "Owin" qui a pour but de fournir des identités et de les authentifier lorsque cela est nécessaire.
L’idée derrière ce composant est de fournir une abstraction assez élevée dans la manière de gérer l’authentification afin que les migrations soient facilitées:
- en v1 on ne propose qu’un login/mot de passe simplifié,
- en v2 on propose l’authentification par google, twitter et facebook,
- en v3 on utilise un service de SSO pour que tous nos sites soient unifiés.
Sans ce composant, c’est le contrôleur complet qui doit être redéveloppé à chaque fois. Avec Owin, vous n’avez qu’à développer (si .NET ne le possède pas déjà) un connecteur (en anglais vous trouverez le mot Adapter
voire parfois Bridge
même si il y a des nuances entre les deux) entre votre méthode d’authentification et Owin et le tour est joué. Mieux, vous pouvez même, par soucis de compatibilité proposer deux systèmes en parallèle.
Nous ne personnaliserons pas ce contrôleur dans ce tutoriel, je terminerai simplement cette partie en portant à votre attention la présence du constructeur public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager )
. Ce dernier n’est pas utilisé par ASP.NET il n’est présent que pour vous faciliter la tâche lorsque vous écrirez des tests unitaires.
En effet à partir de ce moment là, il vous suffira d’écrire un mock d’objet ApplicationSignInManager
et de l’envoyer dans le contrôleur pour avoir pouvoir tester votre contrôleur comme il se doit.
Nous reviendront plus tard —et en vidéo, je vous le promet— sur les tests qu’on peut faire sur une application web.
Le modèle de données
Lorsque vous essayez de trouver le modèle de données lié aux utilisateurs, vous pouvez être un peu rassuré : en effet nous retombons sur ce que nous avons eu l’habitude de voir dans notre cours :
- un fichier
IdentityModels.cs
qui va être l’objet de ce paragraphe avec les représentations C# de la base de données ; - un fichier
AccountViewModels.cs
qui contient tous les ViewModel qui permettent d’enregistrer un utilisateur ; - un fichier
ManageViewModel.cs
qui sera dirigé vers les tâche d’administration ou simplement les modifications de son compte par l’utilisateur.
Si nous ne détaillerons pas les fichier de ViewModel, il est important de garder en mémoire que chaque modification des IdentityModels
entraînera une modification des ViewModels et de leur template associé.
Le fichier IdentityModels.cs
contient par défaut deux éléments :
- une classe
ApplicationUser
qui hérite deIdentityUser
; - un contexte de base de données.
Le contexte est un peu spécial car il ne se contente pas d’hériter de DbContext
mais de IdentityDbContext
. Ce qui lui ajoute des méthodes supplémentaires pour interroger la base de données à propos des utilisateurs, à savoir :
Pour ce qui est de notre utilisateur, il s’agit d’une classe simple qui hérite d’un objet déjà défini part ASP.NET et qui est lui-même personnalisable :
Les rôles c’est simplement l’ensemble des niveaux d’autorisation qu’il y a dans votre application.
Si on fait le lien avec zeste de savoir vous avez "trois niveaux" :
- le membre de la communauté,
- le validateur/modérateur,
- l’administrateur technique de la plateforme.
Il suffirait alors de créer trois Role
dans notre base de données qui seraient Community
, Staff
, Technical admin
et on pourrait dès lors simplement promouvoir un membre vers staff en faisant _userManager.AddToRole(user.UserName, "Staff");
dans notre contrôleur et le tour serait joué !
Par défaut, notre utilisateur ne possède que des informations qui permettent de l’identifier et de l’authentifier. C’est donc cet objet qui devra être lié aux objets tel que les articles de blog. Un auteur d’article est un ApplicationUser
.
Néanmoins, ce genre d’objet n’est pas vraiment suffisant. Par exemple, où stocke-t-on l’avatar du membre ? Sa date de naissance (si on en a besoin, n’oubliez pas que c’est une donnée personnelle) ?
Deux philosophies existent alors :
- soit vous augmentez votre
ApplicationUser
de toutes les informations nécessaires, - soit vous créez un second objet qui pourrait être
UserProfile
par exemple où toutes les informations seraient ajoutées et qui serait créé en même temps que l’objetApplicationUser
.
La méthode conseillée par ASP.NET est la première, car en plus de convenir à 99% des cas, elle est plus simple à maintenir.
Le choix de la méthode est très important. Si vous pensez que votre application sort des 99% évoqués au dessus, n’hésitez pas à venir poster la question sur le forum avec le tag [asp.net], nous serons heureux de vous répondre.
Pour ce qui est des rôles, Je vous propose de laisser les choses telles que ASP vous le propose mais sachez qu’il est tout à fait possible de les modifier. Imaginons que nous voulons ajouter un badge à un rôle (comme le badge staff) :
Il suffira ensuite de changer légèrement notre ApplicationDbContext
pour lui indiquer que nous allons changer le format des rôles :
Si dans un de vos contrôleurs vous avez besoin d’accéder aux rôles, il suffit d’utiliser la ligne suivante RoleManager roleManager = new RoleManager<IdentityRole>(new RoleStore<RoleWithAvatar>(context));
[Mini TP] Restreindre les accès
Peut être vous souvenez-vous de ce conseil : "Il est fortement conseillé de mettre [Authorize]
sur toutes les classes de contrôleur puis de spécifier les méthodes qui sont accessibles publiquement à l'aide de
[AllowAnonymous]`". C’est maintenant que nous allons pleinement le mettre en œuvre.
Comme nous sommes arriver assez loin dans ce tutoriel, vous devriez, avec un minimum d’information réussir le mini tp qui arrive.
Les attributs Authorize et AllowAnonymous sont des filtres d’autorisation. Nous reverrons plus tard le fonctionnement des filtres en général, mais l’idée c’est que ces attributs permettent d’appliquer un certains nombres d’actions avant que le code métier ne soit exécuté. Ils vérifient avec le connecteur Owin si l’utilisateur qui est en train de visité la page a bien ouvert une session authentifiée.
Notre but sera de proposer notre propre attribut d’autorisation qui fera deux vérifications :
- premièrement le visiteur est bien authentifié (comme
AuthorizeAttribute
) - deuxièmement le visiteur appartient à un groupe qu’on aura défini (par exemple Staff)
L’idée serait qu’à la fin on ait une action du style :
// POST: /Manage/BanUser
[HttpPost]
[ValidateAntiForgeryToken]
[AuthorizedFor("Staff")]
public async Task<ActionResult> BanUser(BanUserViewModel model)
{
//etc.
}
En ce qui concerne les IAuthorizeAttribute
, ils nous propose de surcharger une méthode appelée OnAuthentication
qui ne renvoie rien. Pourtant, elle peut modifier son paramètre filterContext
pour y spécifier l’attribut Result
lorsque l’autorisation a échouée. Il suffira de lui dire que ce résultat est un HttpUnauthorizedResult
.
Si vous n’y arrivez pas n’hésitez pas à venir sur le forum avant de regarder la correction.
-
Et n’oubliez pas de bien définir votre mot de passe.
↩
Vous voilà mieux armé pour gérer les utilisateurs de votre site. Dans une prochaine version de ce tutoriel vous aurez droit à une vidéo de démonstration vous permettant d’ajouter une authentification via les compte facebook, twitter ou encore google.