Procedimiento para obtener todas las notificaciones de usuarios a la hora del aumento de notificaciones en SharePoint 2010






Artículo original publicado el miércoles, 30 de marzo de 2011


Un obstáculo bastante frecuente a la hora de hacer aumento de notificaciones en SharePoint 2010 ha sido intentar comprender qué notificaciones tiene un usuario cuando el proveedor de notificaciones personalizado se invoca para hacer aumento de notificaciones.  Por ejemplo, las notificaciones que se van a aumentar para un usuario pueden depender del valor de otras notificaciones que este tiene. Es decir, si el usuario pertenece al rol “Admins. del dominio”, agregue la notificación de rol “usuario superior”; de lo contrario, agregue la notificación “usuario estándar”.  Después de estar frustrado por esto durante mucho tiempo, a mis buenos amigos, Israel V. y Matt Long, se les ocurrió finalmente la solución para este engorroso problema (y merecen el 100% del crédito por esta solución: ¡gracias, amigos!).


 


Uno de los problemas subyacentes al tratar de obtener esta información fuera de los parámetros proporcionados cuando el proveedor de notificaciones se invoca para el aumento es que no se tiene acceso a un HttpContext para poder ver la colección de notificaciones.  Israel y Matt entendieron correctamente esto y dieron con la alternativa, que es el OperationContext.  ¿Y de qué se trata?


 


OperationContext tiene una buena introducción aquí:  http://msdn.microsoft.com/en-us/library/system.servicemodel.operationcontext(v=VS.90).aspx.  En pocas palabras, lo que nos interesa es que permite el acceso a encabezados de mensajes entrantes y propiedades (en el caso de los usuarios de SAML) y al contexto de seguridad (en el caso de los usuarios de Windows).  ¿De qué manera nos ayuda esto?  Cuando el proveedor de notificaciones personalizado se invoca para el aumento, se puede obtener esta información de mensaje entrante para los usuarios de SAML con el siguiente aspecto:


 


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


   <s:Header>


       <a:Action s:mustUnderstand=1>http://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>http://www.w3.org/2005/08/addressing/anonymous</a:Address>


       </a:ReplyTo>


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


   </s:Header>


   <s:Body>


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


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


              <a:EndpointReference>


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


              </a:EndpointReference>


          </wsp:AppliesTo>


          <trust:KeyType>http://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=http://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=http://schemas.xmlsoap.org/ws/2005/05/identity/claims>


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


                     </saml:Attribute>


                     <saml:Attribute AttributeName=role AttributeNamespace=http://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=http://schemas.microsoft.com/ws/2008/06/identity/claims>


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


                     </saml:Attribute>


                     <saml:Attribute AttributeName=primarysid AttributeNamespace=http://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=http://www.w3.org/2000/09/xmldsig#>


                     <ds:SignedInfo>


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


                        </ds:CanonicalizationMethod>


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


                        </ds:SignatureMethod>


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


                           <ds:Transforms>


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


                               </ds:Transform>


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


                               </ds:Transform>


                           </ds:Transforms>


                           <ds:DigestMethod Algorithm=http://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=http://www.w3.org/2000/09/xmldsig#>


                        <X509Data>


                           <X509Certificate>MIIFlzCCBH+gAwIBAgIKHmblahblahblah</X509Certificate>


                        </X509Data>


                     </KeyInfo>


                 </ds:Signature>


              </saml:Assertion>


          </trust:OnBehalfOf>


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


       </trust:RequestSecurityToken>


   </s:Body>


</s:Envelope>


 


<<Una disculpa por el largo y feo fragmento de XML, pero quería que lo vieran>>


 


Ahora, ya que tenemos un fragmento de XML con las notificaciones insertadas, no es un problema abrirlas y usarlas durante el aumento.  Este es un pequeño ejemplo rápido que escribí para hacerlo:


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”, “http://www.w3.org/2003/05/soap-envelope”);


       xNS.AddNamespace(“trust”, “http://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



}


 


Eso es todo.  El código es bastante simple y está tan comentado que creo que si mira y pega el XML en un buen editor de XML (como el que viene con Visual Studio .NET), quedará claro cómo funciona.  No tiene ninguna ciencia en este momento, simplemente se trata de XML.


 


También hay un efecto secundario interesante que se puede implementar en función de este modelo, como comenta el lector del blog, Luis A.  El conjunto de notificaciones que se obtienen en RequestMessage incluye todo lo que proviene de IP-STS, incluidas las notificaciones que SharePoint STS eliminará si no se asigna una notificación correspondiente.  Por lo tanto, si sabe qué notificaciones va a eliminar SharePoint y desea conservarlas de todos modos, simplemente puede volver a aumentarlas con el proveedor de notificaciones personalizado.  Solo tiene que extraer de RequestMessage los tipos y valores de las notificaciones, y vuélvalas a agregar.


 


Los usuarios de notificaciones de Windows no tendrán este mensaje asociado con la solicitud, pero se puede obtener un WindowsIdentity para ellos mediante el SecurityContext del OperationContext.  Desde ahí, puede hacer todas las cosas disponibles con un WindowsIdentity, como obtener la lista de grupos a los que pertenece el usuario, etc.  Este es un ejemplo:


 


//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(“http://schemas.microsoft.com/ws/2008/06/identity/claims/role”,


              groupName));


}


 


Tal vez desee asegurarse de que agrega una instrucción using para System.Security.Principal para obtener WindowsIdentity, IdentityReference, NTAccount, etc. a fin de resolver correctamente.  De lo contrario, el código debería ser bastante sencillo.  Simplemente voy a obtener la lista de grupos del usuario y los voy a colocar en mi colección personalizada de notificaciones como  una notificación de rol estándar.


 


Gracias otra vez, Israel y Matt , por compartir esta información tan valiosa.



Esta entrada de blog es una traducción. Puede consultar el artículo original en How to Get All User Claims at Claims Augmentation Time in SharePoint 2010


Comments (0)