Abrufen aller Benutzeransprüche zum Zeitpunkt der Anspruchserweiterung in SharePoint 2010

Veröffentlichung des Originalartikels: 30.03.2011

Eine relativ konstante Hürde bei der Anspruchserweiterung in SharePoint 2010 war bislang das Herausfinden, welche Ansprüche ein Benutzer hat, wenn Ihr benutzerdefinierter Anspruchsanbieter für die Anspruchserweiterung aufgerufen wird. Die Ansprüche, die Sie für eine Person erweitern möchten, können beispielsweise vom Wert anderer Ansprüche des Benutzers abhängen. Wenn die Person z. B. zur Gruppe der Domänen-Administratoren gehört, fügen Sie den Rollenanspruch "Superbenutzer" und andernfalls den Anspruch "Standardbenutzer" hinzu. Nachdem mich dieser Umstand lange Zeit frustriert hat, haben mein guter Freund Israel V. und Matt Long letztlich eine Lösung für dieses schwierige Problem gefunden (sodass ihnen diese Lösung zu 100 % anzurechnen ist. Nochmals vielen Dank, Jungs!).

 

Eines der grundlegenden Probleme beim Versuch, diese Informationen außerhalb der Parameter zu erhalten, die bereitgestellt werden, wenn Ihr Anspruchsanbieter zwecks Erweiterung aufgerufen wird, ist, dass es keinerlei Zugriff auf ein HttpContext-Objekt gibt, um die Auflistung von Ansprüchen zu untersuchen. Israel und Matt haben genau dies festgestellt und mit dem OperationContext-Objekt eine Alternative ermittelt. Worum handelt es sich dabei?

 

Für das OperationContext-Objekt finden Sie hier eine gute Übersicht: https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext(v=VS.90).aspx. Zusammenfassend ermöglicht uns dieses Objekt den Zugriff auf Kopfzeilen und Eigenschaften eingehender Nachrichten (für SAML-Benutzer) und den Sicherheitskontext (für Windows-Benutzer). Wie kann es uns helfen? Wenn also Ihr benutzerdefinierter Anspruchsanbieter zwecks Erweiterung aufgerufen wird, können Sie für SAML-Benutzer in eingehenden Nachrichten die folgenden Informationen erhalten:

 

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

<<Entschuldigung für diesen zugegebenermaßen langen unschönen XML-Codeauszug, den ich Ihnen aber unbedingt zeigen wollte>>

 

Nun da wir über einen XML-Codeauszug verfügen, in den unsere Ansprüche eingebettet sind, ist es nicht weiter schwierig, diese auszupacken, um sie während der Erweiterung zu verwenden. Es folgt ein kurzer Beispielcode, den ich genau zu diesem Zweck geschrieben habe:

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)

{

//Den Anforderungsumschlag mit Anspruchsinformationen abrufen

       string rqst =

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

       //Eine Liste zum Speichern der Ergebnisse erstellen

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

                          

       //Ein XML-Dokument zum Analysieren der Ergebnisse und Laden der Daten erstellen

       XmlDocument xDoc = new XmlDocument();

       xDoc.LoadXml(rqst);

       //Eine neue Namespacetabelle erstellen, die abgefragt werden kann

       XmlNamespaceManager xNS = new XmlNamespaceManager(xDoc.NameTable);

       //Die Namespaces hinzufügen, die verwendet werden sollen

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

                    

       //Die Liste der Anspruchsknoten abrufen

       XmlNodeList xList =

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

       //Die Übereinstimmungen abrufen

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

       {

              //Die Übereinstimmungen durchzählen, um die Anspruchsliste abzurufen

              foreach (XmlNode xNode in xList)

              {

                     //Zunächst den Anspruchstyp abrufen

                     string currentClaimType =

                           xNode.Attributes["AttributeNamespace"].Value +

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

                     //Jeder Anspruchstyp verfügt über 1:n untergeordnete Knoten mit den Werten

                     foreach (XmlNode claimNode in xNode.ChildNodes)

                     {

                           incomingClaims.Add(new IncomingClaim(currentClaimType,

                                  claimNode.InnerText));

                     }

              }

       }

//Nun können Sie mit der Anspruchsliste tun, was immer Sie möchten, und zum Abschluss kommen

//Ihre Erweiterung

}

 

Das war's auch schon. Der Code ist relativ einfach und so ausführlich kommentiert, dass ich glaube, dass wenn Sie ihn in einen benutzerfreundlichen XML-Editor (wie den in Visual Studio .NET) einfügen, Sie gut erkennen können, wie er funktioniert. Keine Raketenwissenschaft an dieser Stelle, nur XML.

 

Und wie Blogleser Luis A. herausstellt, gibt es außerdem einen interessanten Nebeneffekt, den Sie basierend auf diesem Muster implementieren können. Die Gruppe der Ansprüche, die Sie im RequestMessage-Objekt erhalten, enthält alles, das aus Ihrem IP STS stammt, enschließlich aller Ansprüche, die der SharePoint STS löscht, wenn es keinen entsprechend zugeordneten Anspruch gibt. Wenn Sie also wissen, welche Ansprüche SharePoint löschen wird und Sie diese trotzdem beibehalten möchten, können Sie sie selbst mithilfe Ihres benutzerdefinierten Anspruchsanbieters erneut erweitern. Rufen Sie die Anspruchswerte und -typen aus dem RequestMessage-Objekt ab, und fügen Sie sie ihm wieder hinzu.

 

Bei Windows-Benutzern der anspruchsbasierten Authentifizierung wird diese Nachricht nicht ihrer Anforderung zugeordnet, aber Sie können ein WindowsIdentity-Objekt für sie mithilfe des SecurityContext-Objekts des OperationContext-Kontexts abrufen. Anschließend können Sie alle Aufgaben ausführen, die mit einem WindowsIdentity-Objekt möglich sind, z. B. die Liste der Gruppen abrufen, denen der Benutzer angehört, usw. Hier ein Beispiel:

 

//Für Windows

WindowsIdentity wid =

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

//Verweis auf die Gruppen abrufen, denen der Benutzer angehört

IdentityReferenceCollection grps = wid.Groups;

//Alle Gruppen durchzählen (die jeweils als SID dargestellt werden)

//Dies schließt ALLE Gruppen ein - vordefinierte, lokale, Domänengruppen usw.

foreach (IdentityReference grp in grps)

{

       //Die einfachen Namen der Gruppe abrufen

       SecurityIdentifier sid = new SecurityIdentifier(grp.Value);

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

       string groupName = groupAccount.ToString();

       //Bei Domänengruppen die Domäne\das Präfix aus der Gruppe entfernen

       //In Reihenfolge benennen, um für Funktionsparität mit SAML-Benutzern zu sorgen

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

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

       //Den Anspruch hinzufügen

       incomingClaims.Add(new

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

              groupName));

}

 

Fügen Sie auf jeden Fall eine USING-Anweisung für System.Security.Principal hinzu, um WindowsIdentity, IdentityReference, NTAccount usw. ordnungsgemäß aufzulösen. Ansonsten sollte der Code wiederum unkompliziert sein. Ich rufe lediglich die Gruppen für den Benutzer ab und lege sie in meiner benutzerdefinierten Auflistung von Ansprüchen als einen standardmäßigen Rollenanspruch ab.

 

Nochmals Dank an Israel und Matt für diese überaus nützlichen Informationen.

Es handelt sich hierbei um einen übersetzten Blogbeitrag. Sie finden den ursprünglichen Artikel unter How to Get All User Claims at Claims Augmentation Time in SharePoint 2010.