Impersonation dans SharePoint – passer une fonction en paramètre

Dernièrement j’ai dû refaire de l’impersonation dans SharePoint.

Nous connaissons tous les lignes de code suivantes qui permettent d’exécuter du code avec des droits plus élevés :

SPSecurity.RunWithElevatedPrivileges(delegate()
{
   using (SPSite elevatedSite = new SPSite(SPContext.Current.Site.Id))
   {
      using (SPWeb elevatedWeb = elevatedSite.OpenWeb(SPContext.Current.Web.Id))
      {
          // code exécuté avec des droits élevés
      }
   }
});

Mon besoin était différent, je voulais effectuer une action sous l’identité d’un utilisateur spécifique. Une recherche m’a permis de trouver l’article Les secrets de l’impersonation dans SharePoint de Guillaume Meyer. Même s’il date de 2013, l’article conserve tout son intérêt.

SPWeb oWeb = SPContext.Current.Web;
SPUserToken token = oWeb.AllUsers[@”domainaccount].UserToken;
using (SPSite elevatedSite = new SPSite(oWeb.Site.ID, token))
 {
    using (SPWeb elevatedweb = site.OpenWeb())
     {
      // code exécuté avec les droits de domainaccount
     }
 }

Vous vous en doutez, je voulais aller un peu plus loin en fournissant un moyen pour exécuter n’importe quelles actions sous l’identité d’un autre compte.

// permet d’exécuter une fonction dans le contexte d’un autre utilisateur
// les parametres sont passés à la fonction
private void switchUser(string siteStr, SPUserToken userToken,
                        Action<SPWeb, object[]> codeToExecute,
                        params object[] parameters)
{
    using (SPSite s = new SPSite(siteStr, userToken))
    {
        SPWeb w = s.OpenWeb();
        codeToExecute(w, parameters);
    }
}

private void MonTraitement(SPWeb w, object[] parameters)
{
    // w : permet de travailler sur le site avec l’autre utilisateur
    // le tableau de paramètre est spécifique à la fonction
    // il faut sécuriser la récupération
    Guid listid = (Guid)parameters[0] ;
    int listitemid = (int)parameters[1];
    string document = parameters[2] as string;

    // le code du traitement
    // ou s'il s'agit d'une fonction homonyme qui sert de wrapper
    // MonTraitement(listid, listitemid, document);
}

private void CallExample()
{
    // …

    // definer la function à exécuter
    Action<SPWeb, object[]> traitement = MonTraitement;
    // créer le tableau de parametre
    object[] parameters = new object[3];
    parameters[0] = properties.ListId;
    parameters[1] = properties.ListItemId;
    parameters[2] = document;
    // récuperer l’utilisateur à impersoner
    SPUser user = web.EnsureUser(responsable);
    // exécuter le traitement sous l’identiter d’un autre
    switchUser(site.Url, user.UserToken, traitement, parameters );
}

Voici un bout de code utile qui devrait faciliter certaines situations.