Migrieren von Benutzerkonten von Windows-Ansprüchen zu SAML-Ansprüchen
Migrieren von Benutzerkonten von Windows-Ansprüchen zu SAML-Ansprüchen
In letzter Zeit erhielt ich viele Anfragen von Benutzern, die zunächst Windows-Ansprüche verwenden und später auf SAML-Ansprüche umstellen möchten. Das klingt ziemlich logisch, aber leider gibt es keine vorgefertigte Methode, um Konten von Windows-Ansprüchen zu SAML-Ansprüchen zu migrieren. Die gute Nachricht ist die im kumulativen Update vom August 2010 hinzugefügte SharePoint-Produktgruppe, mit der Sie Ihren eigenen benutzerdefinierten Code in der MigrateUsers-Methode ausführen können. Demnächst wird ein umfangreiches Dokument zur Anwendungsprogrammierschnittstelle (Application Programming Interface, API) zusammen mit einem Codebeispiel von Bryan P. und Raju S. verfügbar sein, und mein Codebeispiel basiert darauf. Sie haben die neue API (in Wirklichkeit eine Schnittstelle – IMigrateUserCallback) hervorragend dokumentiert, weshalb ich hier nicht ausführlich darauf eingehen werde. Sobald ich über den Link zu den neu veröffentlichten Informationen verfüge, werde ich diesen Blogbeitrag entsprechend aktualisieren.
Wie gewöhnlich beginne ich zunächst mit dem Einfügen des Codes meiner benutzerdefinierten Migrationsklasse. Anschließend werde ich die meiner Meinung nach interessanten Komponenten behandeln.
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(
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("https://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;
}
}
}
Als erstes überprüfe ich den Wert des übergebenen SPWebApplication.AuthenticationMethod-Parameters. Ich bin nur am Konvertieren von anspruchsbasierten Benutzern (Windows zu SAML) interessiert, weshalb dies die einzige Situation ist, in der Code ausgeführt werden muss. Wenn der aktuelle Benutzer ein anspruchsbasierter Benutzer ist, muss ich zunächst einen Verweis auf das lokale SPClaimProviderManager-Element abrufen, um eine anspruchsbasierte Darstellung des Benutzers zu erhalten. Auf diese Weise kann ich bestimmen, ob es sich um einen Benutzer für Windows-Ansprüche, FBA-Ansprüche oder SAML-Ansprüche handelt. In meinem Fall möchte ich nur Benutzer konvertieren, die Windows-Ansprüche verwenden.
Nachdem ich festgestellt habe, ob einer dieser Anspruchstypen vorhanden ist, muss ich im nächsten Schritt herausfinden, ob der Anspruch für einen Benutzer oder eine Gruppe gilt. Dabei ist Folgendes merkwürdig. Selbst wenn der aktuelle Benutzer eine Gruppe für Windows-Ansprüche ist, gibt der an die Methode übergebene isGroup-Parameter false zurück. Das heißt, ich muss selbst überprüfen, ob es sich bei der aktuellen „Entität“ um einen Benutzer oder eine Gruppe handelt. Ich betrachte demnach einfach den Anspruchswert. Wenn es sich um einen Benutzer handelt, liegt das Format Domäne\Benutzer vor; andernfalls handelt es sich um eine Gruppe, die ein SID-Format aufweist.
Nachdem ich nun den Entitätstyp kenne, kann ich bestimmen, welcher Anspruchstyp erforderlich ist. Für einen Benutzer muss ich einen Identitätsanspruch erstellen. Unter anderem muss ich den Namen von SPTrustedIdentityTokenIssuer kennen, der in der Webanwendung verwendet wird. Ich hätte hierfür Code schreiben können, aber es handelt sich ja nur um ein Codebeispiel, weshalb Sie den richtigen Namen an den Konstruktor für meine Klasse übergeben müssen. Ich verwende also den Anmeldenamen des Benutzers (nach der Domänenkomponente), und für meine Zwecke lautet die E-Mail-Adresse immer loginname@contoso.com. Wenn dies nicht Ihrer Organisation entspricht, müssen Sie mithilfe eigener Methode die richtige E-Mail-Adresse bestimmen. Mithilfe des obigen Codes erstelle ich einen Identitätsanspruch für diesen Benutzer, und dies ist der zurückgegebene Wert – und in diesen Wert wird in diesem Fall das Konto vbtoys\darrins konvertiert.
Für Gruppen verwende ich die an mich übergebene SID, und mithilfe der NTAccount-Klasse rufe ich den Anzeigenamen der Gruppe ab. Damit erstelle ich einen neuen Rollenanspruch und rufe dann von dort den codierten Wert als den Wert ab, zu dem die Gruppe migriert werden soll.
Ich möchte außerdem darauf hinweisen, dass ich für die Benutzer und Gruppen nicht automatisch versuche, alles zu migrieren. Sie können einige Dinge migrieren, wie z. B. Dienstkonten, integrierte Konten usw. Von Ihren Anforderungen hängt es ab, ob Sie migrieren. Es ist übrigens sehr praktisch, dass Sie diese Migrationsmethode beliebig oft wiederholen können. Sie können natürlich auch nur eine Teilmenge der Benutzer migrieren oder die Migration per Batchverarbeitung oder schrittweise vornehmen. Angenommen, Sie verfügen über eine Datenbank mit allen Benutzern, die Sie migrieren sollen. Sie könnten für diese Datenbank die Liste der Benutzer abfragen. Beim Abrufen der einzelnen Benutzer im Migrationscode könnten Sie dann überprüfen, ob der Benutzer in der Liste der Benutzer aus Ihrer Datenbank vorhanden ist. Dies ist nur ein mögliches Beispiel.
Ich möchte mich nicht wirklich mit der SDK-Dokumentation von Bryan und Raju befassen, möchte aber zumindest erläutern, wie Ihre Klasse aufgerufen wird, damit Sie sich nicht allein gelassen vorkommen. Ich habe einfach eine Winforms-Anwendung geschrieben und ihr einen Projektverweis auf meine oben beschriebene benutzerdefinierte Assembly hinzugefügt. Dies erleichtert das gleichzeitige Erstellen und Debuggen erheblich. Der Code zum Aufrufen meiner Klasse und zum Ausführen einer Migration sieht so oder ähnlich aus:
//get a reference to my web application
SPWebApplication wa = SPWebApplication.Lookup(new Uri("https://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);
Das ist alles. Für die verschiedenen Szenarien gibt es viele weitere Varianten, aber das ist schon mal ein guter Ausgangspunkt. Und Bryan und Raju werden noch viel mehr Informationen hinzufügen.
Es handelt sich hierbei um einen übersetzten Blogbeitrag. Sie finden den Originalartikel unter Migrating User Accounts from Windows Claims to SAML Claims