Получение всех пользовательских утверждений во время дополнения утверждений в SharePoint 2010

 

Исходная статья опубликована в среду 30 марта 2011 г.

Довольно часто возникающая трудность, возникающая при дополнении утверждений в SharePoint 2010, связана с попыткой выяснить, какие утверждения имеются у пользователя при вызове настраиваемого поставщика утверждений для дополнения утверждений. Например, утверждения, которые нужно дополнить для пользователя, могут зависеть от значения других имеющихся утверждений. Например, если пользователь принадлежит роли "Администраторы домена", нужно добавить утверждение роли "Опытный пользователь", а в противном случае нужно добавить утверждение "Обычный пользователь". Это неудобство довольно долго преследовало меня, пока мой друг Израэль В. и Мэтт Лонг (Matt Long) не придумали решение для этой проблемы (это их заслуга на 100%, спасибо вам, ребята!).

 

Одна из базовых проблем при попытке получить данную информацию вне предоставленных параметров, если поставщик утверждений вызван для дополнения, связана с отсутствием доступа к HttpContext для поиска коллекции утверждений. Израэль и Мэтт придумали альтернативу, OperationContext. Так что это?

 

Что ж, OperationContext хорошо описывается здесь: https://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext(v=VS.90).aspx. В двух словах, нас интересует та штука, которая предоставляет доступ к заголовкам и свойствам входящих сообщений (для пользователей SAML) и контексту безопасности (для пользователей Windows). Как это нам поможет? При вызове настраиваемого поставщика утверждений для дополнения можно получить сведения о входящем сообщении для пользователей 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 Алгоритм="https://www.w3.org/2001/04/xmldsig-more#rsa-sha256">

                        </ds:SignatureMethod>

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

                           <ds:Transforms>

                               <ds:Transform Алгоритм="https://www.w3.org/2000/09/xmldsig#enveloped-signature">

                               </ds:Transform>

                               <ds:Transform Алгоритм="https://www.w3.org/2001/10/xml-exc-c14n#">

                               </ds:Transform>

                           </ds:Transforms>

                           <ds:DigestMethod Алгоритм="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>

<<извиняюсь за длинный кусок кода Xml, но я хотел показать его вам>>

 

Итак, у нас есть фрагмент кода Xml со встроенными утверждениями, их можно легко извлечь и использовать при дополнении. Вот небольшой пример, который был написан мною:

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)

{

//получение конверта запроса с данными об утверждениях

       string rqst =

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

       //создание списка для хранения результатов

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

                          

       //создание XML-документа для анализа результатов и загрузки данных

       XmlDocument xDoc = new XmlDocument();

       xDoc.LoadXml(rqst);

       //создание новой таблицы пространства имен для выполнения запроса

       XmlNamespaceManager xNS = new XmlNamespaceManager(xDoc.NameTable);

       //добавление пространств имен, которые будут нами использоваться

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

                    

       //получение списка узлов утверждений

       XmlNodeList xList =

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

       //получение совпадений

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

       {

              //перечисление совпадений для получения списка утверждений

              foreach (XmlNode xNode in xList)

              {

                     //получение списков типов утверждений

                     string currentClaimType =

                           xNode.Attributes["AttributeNamespace"].Value +

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

                     //у каждого типа утверждения будут дочерние узлы "от одного к многим" со значениями

                     foreach (XmlNode claimNode in xNode.ChildNodes)

                     {

                           incomingClaims.Add(new IncomingClaim(currentClaimType,

                                  claimNode.InnerText));

                     }

              }

       }

//теперь со списком утверждений можно сделать все, что угодно, и завершить работу

//дополнения

}

 

Вот, в принципе и все. Этот код довольно просто и подробно прокомментирован, и если его просмотреть и вставить XML в хороший XML-редактор (например, тот, который поставляется с Visual Studio .NET), можно будет легко понять, как он работает. Пока никаких сложностей, просто XML.

 

Существует также один интересный побочный эффект, который можно реализовать на основе этого шаблона, как заметил читатель данного блога Луис А. Набор утверждений, получаемый в RequestMessage, содержит все утверждения, поступившие от IP-STS, в том числе все утверждения, удаляемые SharePoint STS, если на найдены соответствующие им утверждения. Поэтому, если знать, какие утверждения SharePoint будут удалены, их можно сохранить, просто дополнив с помощью настраиваемого поставщика утверждений. Просто извлеките значения и типы утверждений из RequestMessage и добавьте их назад.

 

Для пользователей утверждений Windows это сообщение не будет связано с запросом, но при этом можно получить WindowsIdentity, используя SecurityContext элемента OperationContext. Затем можно выполнить любое действие, доступное для WindowsIdentity, например получить список групп, в которые входит пользователь и т. д. Вот соответствующий пример:

 

//обработка окон

WindowsIdentity wid =

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

//получить ссылку на группы, в которые входит пользователь

IdentityReferenceCollection grps = wid.Groups;

//перечисление каждой группы (которые представлены как SID)

//Примечание. Сюда входят ВСЕ группы — встроенные, локальные, доменные и т. д.

foreach (IdentityReference grp in grps)

{

       //получение простого имени группы

       SecurityIdentifier sid = new SecurityIdentifier(grp.Value);

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

       string groupName = groupAccount.ToString();

       //для доменных групп удаляет префикс "домен\" из группы

       //имя для паритета возможностей с пользователями SAML

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

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

       //добавление утверждения

       incomingClaims.Add(new

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

              groupName));

}

 

Необходимо добавить оператор using для System.Security.Principal, чтобы получить элементы WindowsIdentity, IdentityReference, NTAccount и т. д. для правильного их разрешения. В противном случае код будет очень простым. Я просто получаю список групп пользователя и размещаю их в своей коллекции утверждений как стандартные утверждения роли.

 

Еще раз благодарю Израэля и Мэтта за предоставление этой суперценной информации.

Это локализованная запись блога. Исходная статья доступна по адресу How to Get All User Claims at Claims Augmentation Time in SharePoint 2010