Migración de cuentas de usuario de notificaciones de Windows a notificaciones de SAML

Últimamente en el trabajo he estado atendiendo a personas que están interesadas en comenzar como usuarios de notificaciones de Windows y luego en algún momento pasar a usar notificaciones de SAML.  Suena bastante razonable, pero el problema es que no tenemos una solución lista para migrar cuentas de notificaciones de Windows a notificaciones de SAML.  La buena noticia es que el grupo de productos de SharePoint agregó a la actualización acumulada de agosto de 2010 la funcionalidad necesaria para permitir la ejecución de código personalizado en el método MigrateUsers.  Próximamente tendremos un documento completo sobre la API y un código de ejemplo obtenido como resultado del excelente trabajo realizado por Bryan P. y Raju S, y en el que se basa mi ejemplo.  Hicieron un excelente trabajo de documentación de la nueva API (en realidad, una interfaz: IMigrateUserCallback), de modo que intentaré no explayarme demasiado aquí.  En cuanto tenga un vínculo a la información recién publicada al respecto, actualizaré esta entrada.

Por lo tanto, como de costumbre, simplemente pegaré el código de mi clase de migración personalizada y, a continuación, repasaré las partes que considere interesantes.

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.Diagnostics;

using System.Security;

using System.Security.Principal;

//add references to Microsoft.SharePoint and Microsoft.IdentityModel for these

using Microsoft.SharePoint;

using Microsoft.SharePoint.Administration;

using Microsoft.SharePoint.Administration.Claims;

using Microsoft.IdentityModel.Claims;

 

 

namespace MigrateUserSample

{

   public class MigrateTest : IMigrateUserCallback

   {

 

       public string SPTrustedIdentityTokenIssuerName { get; set; }

 

 

       public MigrateTest(string TrustedIdentityTokenIssuerName)

       {

          SPTrustedIdentityTokenIssuerName = TrustedIdentityTokenIssuerName;

       }

 

 

       public string ConvertFromOldUser(string oldUser,

              SPWebApplication.AuthenticationMethod authType, bool isGroup)

       {

          string value = string.Empty;

 

          try

          {

              switch (authType)

              {

                 case SPWebApplication.AuthenticationMethod.Windows:

                     //code for converting from classic Windows would be here

                     Debug.WriteLine(oldUser);

                     break;

                 case SPWebApplication.AuthenticationMethod.Claims:

                     //this is the only scenario this sample will cover

                     //migrating from Windows claims to SAML claims

                     Debug.WriteLine(oldUser);

 

                     //get the claim provider manager

                     SPClaimProviderManager cpm = SPClaimProviderManager.Local;

 

                     //create a claim from the identifier so we can see if the

                     //original issuer came from Windows

                     SPClaim idClaim = cpm.ConvertIdentifierToClaim(oldUser,

                           SPIdentifierTypes.EncodedClaim);

 

                     //this is a Windows claims user, and we are going to

                     //convert to a SAML claims user

                     if (idClaim.OriginalIssuer == "Windows")

                     {

                        //windows claims users will be in the format domain\user;

                        //windows claims groups will be in the SID format

                        if (idClaim.Value.Contains("\\"))

                        {

                           //migrating a user

 

                           //you will want to check the identity of the user here

                           //there may be some Windows claims accounts you don't want to

                           //convert yet, and there will also be service accounts that

                           //are passed in that you may not want to convert either; 

                           //ideally you would just read from a data source to determine

                           //which users you should convert, and then check the identity

                           //here to see if it's one of the users that should be

                           //converted

 

                           //in this case, I'm only converting one user - darrins

                           if (idClaim.Value == "contoso\\darrins")

                           {

                               //I’m getting an identity claim here, grabbing the

                               //part after the "domain\", and appending the email

                               //suffix to it, so it becomes darrins@contoso.com

                               SPClaim migratedUserClaim =

                                         SPClaimProviderManager.CreateUserClaim(

                            q;             idClaim.Value.Split('\\')[1] + "@contoso.com",

                                         SPOriginalIssuerType.TrustedProvider,

                                         SPTrustedIdentityTokenIssuerName);

 

                               //get the encoded value of what the new identity

                               //claim will be

                               value = migratedUserClaim.ToEncodedString();

                           }

                        }

                        else

                        {

                           //migrating a group

 

                           //get the plain name of the group

                           SecurityIdentifier sid =

                               new SecurityIdentifier(idClaim.Value);

                           NTAccount groupAccount =

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

 

                           string groupName = groupAccount.ToString();

 

                           //only interested in migrating the Portal People group

                           if (groupName.ToLower() == "contoso\\portal people")

                           {

                               //create a new role claim

                               SPClaim migratedGroupClaim =

                                  new SPClaim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role",

                                  groupName.Split('\\')[1],

                                  Microsoft.IdentityModel.Claims.ClaimValueTypes.String,

                                  SPOriginalIssuers.Format(

                                  SPOriginalIssuerType.TrustedProvider,

                                  SPTrustedIdentityTokenIssuerName));

 

                               //get the encoded value of what the new role claim will be

                               value = migratedGroupClaim.ToEncodedString();

                           }

                        }

                     }

 

                     break;

                 case SPWebApplication.AuthenticationMethod.Forms:

                     //code for converting from Forms would be here

                     Debug.WriteLine(oldUser);

                     break;

              }

          }

          catch (Exception ex)

          {

              Debug.WriteLine(ex.Message);

          }

 

          return value;

       }

   }

}

 

 

Lo primero que hago es comprobar el valor del parámetro SPWebApplication.AuthenticationMethod que se pasó.  Dado que solo me interesa convertir usuarios de notificaciones (de Windows a SAML), esa es la única situación en la que tengo un código para ejecutar.  Cuando el usuario actual es un usuario de notificaciones, lo primero que hago es obtener una referencia al SPClaimProviderManager local para poder obtener una representación de notificación del usuario.  Mi objetivo es determinar si el usuario es un usuario de notificaciones de Windows, un usuario de notificaciones de FBA o un usuario de notificaciones de SAML.  En este caso, solo deseo convertir usuarios que sean usuarios de notificaciones de Windows. 

 

Después de determinar que es uno de estos usuarios, lo siguiente es averiguar si la notificación es para un usuario o un grupo.  Y entonces se produce una situación extraña.  Incluso cuando el usuario actual es un grupo de notificaciones de Windows, el parámetro isGroup que se pasa en el método devuelve false.  Eso significa que yo mismo debo averiguar si la “entidad” actual es un usuario o un grupo.  Para ello me fijaré en el valor de la notificación: si es un usuario, tendrá el formato dominio\usuario; si, de lo contrario, es un grupo, tendrá un formato de SID.

Ahora que ya sé el tipo de entidad que es, puedo determinar el tipo de notificación que se necesita.  Para un usuario, debo crear una notificación de identidad.  Una de las cosas que debo saber es el nombre de SPTrustedIdentityTokenIssuer que se utiliza en la aplicación web.  Pude haberlo averiguado a través del código pero, como esto es solo un ejemplo, he optado por la opción más cómoda y he obligado al usuario a que me pase el nombre correcto en el constructor de mi clase.  Así que uso el nombre de inicio de sesión del usuario (después de la parte del dominio) y, para este propósito, su dirección de correo electrónico siempre será nombre_inicio_sesión@contoso.com.  Si su organización no se ajusta a este patrón, deberá emplear sus propios medios para determinar la dirección de correo electrónico correcta.  La utilizo con el código anterior para crear una notificación de identidad para el usuario, que es el valor que devuelvo: en este caso, es aquello en lo que se convertirá la cuenta vbtoys\darrins.   

Para los grupos, tomo el SID que recibí y uso la clase NTAccount para obtener el nombre descriptivo del grupo.  Lo uso para crear una nueva notificación de rol y, a continuación, extraigo el valor codificado como el valor al que debe migrar el grupo.   

Otra cuestión importante a tener en cuenta es que tanto para usuarios como para grupos, no migro automáticamente todo.  Es posible que haya algo que no desee migrar, como cuentas de servicio, cuentas integradas, etc.; y esto dependerá de sus necesidades.  Lo fabuloso de este método de migración es que puede ejecutarlo tantas veces como desee.  Si desea migrar solo un subconjunto de usuarios o si desea realizar una migración por lotes, a lo largo del tiempo o cualquier otra posibilidad, puede hacerlo.  Por ejemplo, puede que tenga una base de datos con todos los usuarios que desea migrar.  Puede consultarla para obtener la lista y, a continuación, a medida que se llama a cada usuario en el código de migración, puede comprobar si se encuentra en la lista de usuarios que obtuvo de la base de datos.  Es solo un ejemplo.

No deseo entrar a fondo en la documentación del SDK en la que Bryan y Raju trabajaron arduamente, pero para no dejar esto pendiente, me siento obligado al menos a explicar cómo se invoca la clase.  Lo que hice fue escribir una aplicación de Windows Forms y agregar una referencia de proyecto a mi ensamblado personalizado descrito anteriormente.  De esta forma, resulta extremadamente fácil generar y depurar de forma conjunta.  El código que uso para invocar mi clase y realizar una migración tiene el siguiente aspecto:

 

//get a reference to my web application

SPWebApplication wa = SPWebApplication.Lookup(new Uri("http://foo"));

 

//this is the name of my trusted identity token issuer

string SPTrustedIdentityTokenIssuerName = "ADFSProvider";

 

//create an instance of the custom migrate user callback class

MigrateUserSample.MigrateTest mt =

new MigrateUserSample.MigrateTest(SPTrustedIdentityTokenIssuerName);

 

//create an interface reference to it

IMigrateUserCallback muc = mt as IMigrateUserCallback;

 

//migrate the users with it

wa.MigrateUsers(muc);

 

¡Listo!  Se necesitarán muchas otras variaciones para cubrir todos los escenarios, pero este es un buen punto de partida. Además, Bryan y Raju agregarán mucho más a este cuerpo de trabajo.

Esta entrada de blog es una traducción. Puede consultar el artículo original en Migrating User Accounts from Windows Claims to SAML Claims