Comment obtenir toutes les revendications d’utilisateurs au moment de l’augmentation des revendications dans SharePoint 2010

Date de publication initiale de l’article : mrecredi 30 mars 2011

L’une des difficultés plutôt récurrentes liées à l’augmentation des revendications dans SharePoint 2010 consiste à identifier les revendications que possède un utilisateur lorsque votre fournisseur de revendications personnalisées est appelé pour effectuer une augmentation des revendications. Par exemple, les revendications que vous souhaitez augmenter pour une personne peuvent dépendre de la valeur d’autres revendications de cet utilisateur (par exemple s’il appartient au rôle « Administrateurs de domaine », vous devez ajouter la revendication de rôle « Super utilisateur » ; sinon vous devez ajouter la revendication « Utilisateur standard »). Après une longue période de frustration, mes amis Israel V. et Matt Long sont finalement parvenus à une solution à ce problème épineux (tout le mérite de cette solution leur revenant, j’en profite pour leur envoyer un grand merci !).

 

L’un des problèmes sous-jacents lorsque l’on tente d’obtenir ces informations en dehors des paramètres fournis lorsque votre fournisseur de revendications est appelé pour l’augmentation est le fait que vous n’avez pas accès à un HttpContext pour examiner la collection de revendications. Israel et Matt ont brillament trouvé une alternative, à savoir l’OperationContext. Mais de quoi s’agit-il exactement ?

 

Vous trouverez une bonne vue d’ensemble d’OperationContext à l’adresse suivante : https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext(v=VS.90).aspx. En gros, ce qui est intéressant c’est qu’il nous permet d’accéder aux en-têtes et propriétés des messages entrants (pour les utilisateurs SAML) et au contexte de sécurité (pour les utilisateurs Windows). En quoi ces informations peuvent-elle nous être utiles ? Et bien, lorsque votre fournisseur de revendications personnalisées est appelé pour l’augmentation, vous pouvez accéder à ces informations sur les messages entrants pour les utilisateurs SAML :

 

<s:Envelope xmlns:s="https://www.w3.org/2003/05/soap-envelope" xmlns:a="https://www.w3.org/2005/08/addressing">

   <s:Header>

       <a:Action s:mustUnderstand="1">https://docs.oasis-open.org/ws-sx/ws-trust/200512/RST/Issue</a:Action>

       <a:MessageID>urn:uuid:85a0daaa-2288-4d0a-bda8-5fac05ea61cf</a:MessageID>

       <a:ReplyTo>

          <a:Address>https://www.w3.org/2005/08/addressing/anonymous</a:Address>

       </a:ReplyTo>

       <a:To s:mustUnderstand="1">https://localhost:32843/SecurityTokenServiceApplication/securitytoken.svc</a:To>

   </s:Header>

   <s:Body>

       <trust:RequestSecurityToken xmlns:trust="https://docs.oasis-open.org/ws-sx/ws-trust/200512">

          <wsp:AppliesTo xmlns:wsp="https://schemas.xmlsoap.org/ws/2004/09/policy">

              <a:EndpointReference>

                 <a:Address>https://fc1/</a:Address>

              </a:EndpointReference>

          </wsp:AppliesTo>

          <trust:KeyType>https://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>

          <trust:OnBehalfOf>

              <saml:Assertion MajorVersion="1" MinorVersion="1" AssertionID="_8f1d7b46-2b71-4263-859b-c3e358d7ea84" Issuer="https://myadfsserver/adfs/services/trust" IssueInstant="2011-03-26T18:51:54.671Z" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion">

                 <saml:Conditions NotBefore="2011-03-26T18:51:33.198Z" NotOnOrAfter="2011-03-26T19:51:33.198Z">

                     <saml:AudienceRestrictionCondition>

                        <saml:Audience>urn:sharepoint:fc1</saml:Audience>

                     </saml:AudienceRestrictionCondition>

                 </saml:Conditions>

                 <saml:AttributeStatement>

                     <saml:Subject>

                        <saml:SubjectConfirmation>

                        <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>

                        </saml:SubjectConfirmation>

                     </saml:Subject>

                     <saml:Attribute AttributeName="emailaddress" AttributeNamespace="https://schemas.xmlsoap.org/ws/2005/05/identity/claims">

                        <saml:AttributeValue>testuser@vbtoys.com</saml:AttributeValue>

                     </saml:Attribute>

                     <saml:Attribute AttributeName="role" AttributeNamespace="https://schemas.microsoft.com/ws/2008/06/identity/claims">

                        <saml:AttributeValue>pOregon Marketing</saml:AttributeValue>

   <saml:AttributeValue>Domain Users</saml:AttributeValue>

                        <saml:AttributeValue>pSales</saml:AttributeValue>

                        <saml:AttributeValue>Portal People</saml:AttributeValue>

                     </saml:Attribute>

                     <saml:Attribute AttributeName="windowsaccountname" AttributeNamespace="https://schemas.microsoft.com/ws/2008/06/identity/claims">

                        <saml:AttributeValue>testuser</saml:AttributeValue>

                     </saml:Attribute>

                     <saml:Attribute AttributeName="primarysid" AttributeNamespace="https://schemas.microsoft.com/ws/2008/06/identity/claims">

                        <saml:AttributeValue>testuser@vbtoys.com</saml:AttributeValue>

                     </saml:Attribute>

                 </saml:AttributeStatement>

                 <saml:AuthenticationStatement AuthenticationMethod="urn:federation:authentication:windows" AuthenticationInstant="2011-03-26T18:51:33.069Z">

                     <saml:Subject>

                        <saml:SubjectConfirmation>

                        <saml:ConfirmationMethod>urn:oasis:names:tc:SAML:1.0:cm:bearer</saml:ConfirmationMethod>

                        </saml:SubjectConfirmation>

                     </saml:Subject>

                 </saml:AuthenticationStatement>

                 <ds:Signature xmlns:ds="https://www.w3.org/2000/09/xmldsig#">

                     <ds:SignedInfo>

                        <ds:CanonicalizationMethod Algorithm="https://www.w3.org/2001/10/xml-exc-c14n#">

                        </ds:CanonicalizationMethod>

                        <ds:SignatureMethod Algorithm="https://www.w3.org/2001/04/xmldsig-more#rsa-sha256">

                        </ds:SignatureMethod>

                        <ds:Reference URI="#_8f1d7b46-2b71-4263-859b-c3e358d7ea84">

                           <ds:Transforms>

                               <ds:Transform Algorithm="https://www.w3.org/2000/09/xmldsig#enveloped-signature">

                               </ds:Transform>

                               <ds:Transform Algorithm="https://www.w3.org/2001/10/xml-exc-c14n#">

                               </ds:Transform>

                           </ds:Transforms>

                           <ds:DigestMethod Algorithm="https://www.w3.org/2001/04/xmlenc#sha256">

                           </ds:DigestMethod>

                           <ds:DigestValue>5Qvu+blahblah=</ds:DigestValue>

                        </ds:Reference>

                     </ds:SignedInfo>

                     <ds:SignatureValue>VUSrynYjN8NOcUexqJOCblahblah</ds:SignatureValue>

                     <KeyInfo xmlns="https://www.w3.org/2000/09/xmldsig#">

                        <X509Data>

                           <X509Certificate>MIIFlzCCBH+gAwIBAgIKHmblahblahblah</X509Certificate>

                        </X509Data>

                     </KeyInfo>

                 </ds:Signature>

              </saml:Assertion>

          </trust:OnBehalfOf>

          <trust:RequestType>https://docs.oasis-open.org/ws-sx/ws-trust/200512/Issue</trust:RequestType>

       </trust:RequestSecurityToken>

   </s:Body>

</s:Envelope>

<<Désolé pour ce long et peu élégant segment de code XML, mais je me suis dit qu’il serait utile que vous le voyiez>>

 

Puisque nous avons un segment de code XML où sont incorporées nos revendications, il suffit de les désenvelopper et de pouvoir les utiliser durant l’augmentation. Vous pouvez pour cela utiliser ce petit exemple de code que j’ai rédigé :

using System.Xml;

private class IncomingClaim

{

   public string claimType { get; set; }

   public string claimValue { get; set; }

   public IncomingClaim(string claimType, string claimValue)

   {

   this.claimType = claimType;

       this.claimValue = claimValue;

   }

}

 

protected override void FillClaimsForEntity(Uri context, SPClaim entity,

            List<SPClaim> claims)

{

//get the request envelope with the claims information

       string rqst =

System.ServiceModel.OperationContext.Current.RequestContext.RequestMessage.ToString();

       //create a list to store the results

       List<IncomingClaim> incomingClaims = new List<IncomingClaim>();

                          

       //create an Xml document for parsing the results and load the data

       XmlDocument xDoc = new XmlDocument();

       xDoc.LoadXml(rqst);

       //create a new namespace table so we can query

       XmlNamespaceManager xNS = new XmlNamespaceManager(xDoc.NameTable);

       //add the namespaces we'll be using

       xNS.AddNamespace("s", "https://www.w3.org/2003/05/soap-envelope");

       xNS.AddNamespace("trust", "https://docs.oasis-open.org/ws-sx/ws-trust/200512");

       xNS.AddNamespace("saml", "urn:oasis:names:tc:SAML:1.0:assertion");

                    

       //get the list of claim nodes

       XmlNodeList xList =

xDoc.SelectNodes("s:Envelope/s:Body/trust:RequestSecurityToken/trust:OnBehalfOf/saml:Assertion/saml:AttributeStatement/saml:Attribute", xNS);

       //get the matches

       if ((xList != null) && (xList.Count > 0))

       {

              //enumerate through the matches to get the claim list

              foreach (XmlNode xNode in xList)

              {

                     //get the claim type first

                     string currentClaimType =

                           xNode.Attributes["AttributeNamespace"].Value +

                           "/" + xNode.Attributes["AttributeName"].Value;

                     //each claim type will have one to many child nodes with the values

                     foreach (XmlNode claimNode in xNode.ChildNodes)

                     {

                           incomingClaims.Add(new IncomingClaim(currentClaimType,

                                  claimNode.InnerText));

                     }

              }

       }

//now you can do whatever you want with the list of claims and finish

//your augmentation

}

 

Et voilà, c’est à peu près tout. Ce code est suffisamment clair et les commentaires suffisamment détaillés pour que vous puissiez simplement le coller dans un bon éditeur XML (tel que celui fourni avec Visual Studio .NET). Rien de très spécial à ce stade, simplement du XML.

 

Un lecteur de blog nommé Luis A. a également porté à notre attention un effet secondaire que vous pouvez implémenter. Le jeu de revendications que vous obtiendrez dans le RequestMessage inclut tout ce qui provient de votre service IP-STS, y compris les revendications que le service STS SharePoint supprimera si aucune revendication correspondante n’est mappée. Si vous connaissez les revendications que SharePoint va supprimer et que vous souhaitez les conserver, il vous suffit de les augmenter de nouveau vous-même avec votre fournisseur de revendications personnalisées. Il vous suffit d’extraire les types et valeurs de revendications du RequestMessage et de les rajouter.

 

Pour les utilisateurs de revendications Windows, ce message ne sera pas associé à leur demande, mais vous pouvez obtenir pour eux un WindowsIdentity à l’aide du SecurityContext de l’OperationContext. À partir de là, vous pouvez effectuer toutes les opérations disponibles avec un WindowsIdentity, comme obtenir la liste des groupes auxquels l’utilisateur appartient. Voici un exemple :

 

//do windows

WindowsIdentity wid =

System.ServiceModel.OperationContext.Current.ServiceSecurityContext.WindowsIdentity;

//get a reference to the groups to which the user belongs

IdentityReferenceCollection grps = wid.Groups;

//enumerate each group (which will be represented as a SID)

//NOTE that this includes ALL groups - builtin, local, domain, etc.

foreach (IdentityReference grp in grps)

{

       //get the plain name of the group

       SecurityIdentifier sid = new SecurityIdentifier(grp.Value);

       NTAccount groupAccount = (NTAccount)sid.Translate(typeof(NTAccount));

       string groupName = groupAccount.ToString();

       //for domain groups remove the domain\ prefix from the group

       //name in order to have feature parity with SAML users

       if (groupName.Contains("\\"))

              groupName = groupName.Substring(groupName.IndexOf("\\") + 1);

       //add the claim

       incomingClaims.Add(new

IncomingClaim("https://schemas.microsoft.com/ws/2008/06/identity/claims/role",

              groupName));

}

 

Vous devez absolument ajouter une instruction using pour System.Security.Principal afin d’obtenir WindowsIdentity, IdentityReference, NTAccount, etc. et de résoudre correctement. À part cela, le code ne devrait normalement pas poser de difficulté. J’obtiens simplement la liste des groupes de l’utilisateur et je les place dans ma collection personnalisée de revendications en tant que revendication de rôle standard.

 

Je remercie une nouvelle fois Israel et Matt de nous avoir fait partagé ces informations extrêment précieuses.

Ce billet de blog a été traduit de l’anglais. L’article d’origine se trouve à l’adresse How to Get All User Claims at Claims Augmentation Time in SharePoint 2010