Mettre en place une authentification sur une API REST avec orchard

L'utilisation d'orchard malgré le fait que le CMS suivent les évolutions techniques assez rapidement peut parfois prendre un peu de retard. Ainsi, il n'est pas compliqué d'utiliser de façon basique Web API au sein d'orchard. Pour ceux qui ne connaissent pas la partie Web API, cela s'apparente à un ajout à MVC pour offrir une API REST sans gros effort. Auparavant, il fallait utiliser explicitement le framework WCF pour mettre en place une API en Web service. Même dit REST en s'appuyant sur une exposition IIS avec le protocol HTTP. Bref une démarche lourde, car il faut mettre en place toute la mécanique et la configuration adéquate pour faire fonctionner l'ensemble. Mais avec l'arrivée de MVC 4 la donne change. Et Orchard a intégré cela en permettant de gérer les dépendances des controllers ApiController (si vous ne savez pas de quoi je parle, je vous invite à lire de la documentation sur la MSDN).

Bref, j'ai eu à tester aujourd'hui la mise en place d'un système de sécurité pour une API REST que j'essaie de mettre en place sur mon site.  J'ai vu alors le post d'une personne active sur le CMS Orchard : http://skywalkersoftwaredevelopment.net/blog/orchard-webapi-global-actionfilters-and-dependency-injection. Son utilisation est sommaire, mais elle met bien en avant la difficulté d'injecter les dépendances lors d'usage des attributs permettant de gérer l'accès aux actions des controlleurs de la couche Web API.

Sa solution ne me satisfaisait pas car je ne pouvais pas réutiliser la couche de données d'Orchard. Comme tout informaticien, je suis feignant. Donc dès que je peux éviter de réinventer la roue, je le fais. C'est à ce moment quand j'ai cherché à vouloir injecter des dépendances avec Autofac que je suis tombé sur cette documentation : https://code.google.com/p/autofac/wiki/WebApiIntegration. Seulement, cette intégration n'existe qu'avec la version 3.0 d'Autofac et Orchard utilise dans sa version 1.6 la version 2.6. Pas de bol, je n'ai donc pas creusé davantage cette piste.

J'ai cherché un autre moyen qui ne nécessiterai pas de devoir faire un "fork" du core du CMS (problème de maintenance), mais également que la solution implémentée soit suffisament facile à réutiliser lorsque la librairie d'IOC "Autofac" sera mise à jour. Et finalement, je pense avoir trouvé une astuce pas si mal que ça :

Il faut créer une classe qui servira de base à tous les controlleurs de l'api dérivant donc d'ApiController.

public abstract class AbstractApiController:ApiController  {    }    

Dessus, il faudra ajouter votre méthode qui gérera l'authentification. Si elle a des dépendances, il faudra donc définir le constructeur adéquate pour initialiser ces dépendances.

 

public abstract class AbstractApiController:ApiController      {          #region fields and properties          private readonly IApiSecuredService _apiSecuredService;          #endregion            public AbstractApiController(IApiSecuredService apiSecuredService) {              if(apiSecuredService==null)                  throw new ArgumentNullException("apiSecuredService not should be null");                _apiSecuredService = apiSecuredService;          }            public void OnAuthorize() {              //votre code gérant l'authentification          }      }  

Puis il conviendra alors de créer une classe d'attribut qui dérivera de AuthorizationFilterAttribute. Cela permet de surcharger la méthode OnAuthorization(HttpActionContext actionContext). Ainsi notre classe de base devient :

[ApiKey]  public abstract class AbstractApiController:ApiController      {          #region fields and properties          private readonly IApiSecuredService _apiSecuredService;          #endregion            public AbstractApiController(IApiSecuredService apiSecuredService) {              if(apiSecuredService==null)                  throw new ArgumentNullException("apiSecuredService not should be null");                _apiSecuredService = apiSecuredService;          }            public void OnAuthorize() {              //votre code gérant l'authentification          }      }  

Et on ajoute la nouvelle classe :

public class ApiKeyAttribute : AuthorizationFilterAttribute       {          public override void OnAuthorization(HttpActionContext actionContext) {              var methodAuthorize = actionContext.ControllerContext.ControllerDescriptor.ControllerType.GetMethod("OnAuthorize");              methodAuthorize.Invoke(actionContext.ControllerContext.Controller,null);          }      }  

L'astuce ici consiste à récupérer l'instance de notre controlleur par le biais du contexte de l'action qui va être executé (mais pas encore d'où la force de faire le traitement à ce niveau). A ce moment, le controlleur vu qu'il est instancié a bien toutes ces dépendances à disposition. Nous pouvons donc executer par Reflexion la méthode OnAuthorize que nous avons définit dans la classe de base et donc existe sur tous nos controlleurs composant notre Web API!

L'avantage de procéder ainsi c'est qu'il n'y a pas à appeler explicitement la méthode (risque d'oubli), elle est appellé "automatiquement" via le worflow de traitement des requêtes sur le framework MVC.

Qu'en pensez-vous?


Rejoindre la conversation