Freigeben über


Come ottenere tutte le attestazioni degli utenti durante l'aggiunta di attestazioni in SharePoint 2010

Articolo originale pubblicato mercoledì 30 marzo 2011

Durante l'aggiunta di attestazioni in SharePoint 2010, un problema abbastanza frequente è rappresentato dal tentativo di determinare di quali attestazioni disponga un utente quando il provider di attestazioni personalizzate viene richiamato per eseguire l'aggiunta. Ad esempio, le attestazioni da aggiungere per un utente possono dipendere dal valore di altre attestazioni di cui l'utente già dispone. Se ad esempio la persona appartiene al ruolo "Domain Admins", aggiungere l'attestazione di "utente con privilegi avanzati", altrimenti aggiungere l'attestazione di "utente standard". Dopo lunghi periodi di frustrazione, il mio grande amico Israel V. e Matt Long alla fine sono riusciti a trovare una soluzione per questo difficile problema e meritano il pieno riconoscimento per questa scoperta. Perciò grazie, ragazzi!.

 

Uno dei problemi di fondo che si incontrano tentando di ottenere queste informazioni al di fuori dei parametri forniti quando il provider di attestazioni viene richiamato per l'aggiunta di attestazioni è il fatto di non disporre dell'accesso ad alcun elemento HttpContext per poter esaminare l'insieme di attestazioni. Israel e Matt hanno ben focalizzato questo aspetto e hanno pensato a un'alternativa, ovvero all'elemento OperationContext. Ma, allora, di che si tratta?

 

È possibile trovare una panoramica esauriente su OperationContext all'indirizzo seguente: https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext(v=VS.90).aspx. Per sintetizzare, l'aspetto che ci interessa è che consente di accedere alle proprietà e alle intestazioni dei messaggi in arrivo (per gli utenti SAML) e al contesto di sicurezza (per gli utenti Windows). In che modo tutto ciò può esserci utile? Quando il provider di attestazioni personalizzate viene richiamato per l'aggiunta di attestazioni, per gli utenti SAML è possibile accedere alle informazioni seguenti del messaggio in arrivo:

 

<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>

<<le mie scuse per questo che, mi rendo conto, è un lungo blocco di codice XML non proprio chiarissimo, ma volevo che lo vedeste>>

 

Dal momento che abbiamo un blocco di codice XML con le attestazioni incorporate all'interno, non è poi così difficile eseguire l'unwrapping per poterle utilizzare durante l'aggiunta di attestazioni. Di seguito ecco un breve e rapido esempio che ho scritto a tale scopo:

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

}

 

Ecco fatto. Il codice è sufficientemente semplice e commentato in modo dettagliato. Ritengo perciò che, esaminandolo e incollandolo in un editor XML (come quello fornito con Visual Studio .NET), il funzionamento risulti abbastanza chiaro. Niente fantascienza quindi, ma solo codice XML.

 

In tal modo, è possibile inoltre beneficiare di un interessante vantaggio collaterale evidenziato da un lettore di questo blog, Luis A. Nel set di attestazioni che si otterrà nell'elemento RequestMessage è incluso tutto quanto proviene dal servizio token di sicurezza di provider di identità, comprese le eventuali attestazioni che il servizio token di sicurezza di SharePoint eliminerà se non esiste un'attestazione mappata corrispondente. Se pertanto si conoscono le attestazioni che verranno eliminate da SharePoint e si desidera mantenerle comunque, sarà possibile semplicemente aggiungerle di nuovo con il provider di attestazioni personalizzate. È infatti sufficiente estrarre i tipi e i valori delle attestazioni dall'elemento RequestMessage e quindi aggiungerle di nuovo.

 

Questo messaggio non sarà associato alla richiesta degli utenti di attestazioni di Windows, ma per tali utenti è possibile ottenere un elemento WindowsIdentity utilizzando l'elemento SecurityContext di OperationContext. Da quel punto è quindi possibile eseguire tutte le operazioni consentite con un elemento WindowsIdentity, ad esempio ottenere l'elenco dei gruppi a cui appartiene l'utente e così via. Ecco un esempio:

 

//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));

}

 

Ricordarsi di aggiungere un'istruzione using per System.Security.Principal per ottenere WindowsIdentity, IdentityReference, NTAccount e così via, in modo che la risoluzione avvenga correttamente. In ogni caso, il codice dovrebbe essere di nuovo piuttosto chiaro. Io ottengo l'elenco dei gruppi relativi all'utente e li inserisco nel mio insieme personalizzato di attestazioni come attestazione di ruolo standard.

 

Grazie ancora a Israel e Matt per avere condiviso con noi queste informazioni super preziose.

Questo è un post di blog localizzato. L'articolo originale è disponibile in How to Get All User Claims at Claims Augmentation Time in SharePoint 2010.