Миграция учетных записей пользователей, применяющих утверждения Windows, в учетные записи с утверждениями SAML
Миграция учетных записей пользователей, применяющих утверждения Windows, в учетные записи с утверждениями SAML
В связи с работой, которой я занимался последнее время, многие интересовались тем, каким образом можно начать работу как пользователи с утверждениями Windows и затем переключиться на утверждения SAML. Звучит вполне разумно, но проблема заключается в том, что нет заранее предусмотренного способа миграции учетных записей с утверждениями Windows в учетные записи с утверждениями SAML. Хорошие новости состоят в том, что группа разработчиков SharePoint добавила обработчики в накопительный пакет обновления за август 2010 г., позволяющие выполнять собственный код в методе MigrateUsers. Готовится к выходу целый документ, посвященный данному API, с примером кода — это результат отличной работы Брайана П. (Bryan P.) и Раджу С. (Raju S.). Мой пример основан на данном документе. Они проделали прекрасную работу, задокументировав новый API (фактически, интерфейс IMigrateUserCallback), поэтому я не буду его подробно описывать. Как только у меня появится ссылка на новые опубликованные сведения по этому вопросу, я добавлю ее в данную запись блога.
Как обычно, я начну со вставки кода настраиваемого класса миграции и пошагово опишу самые интересные части кода.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.Security;
using System.Security.Principal;
//добавление ссылок на Microsoft.SharePoint и Microsoft.IdentityModel
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:
//код для преобразования из классики Windows
Debug.WriteLine(oldUser);
break;
case SPWebApplication.AuthenticationMethod.Claims:
//это единственный сценарий, в примере, охватывающий
//миграцию с утверждений Windows утверждения в SAML
Debug.WriteLine(oldUser);
//получение диспетчера поставщиков утверждений
SPClaimProviderManager cpm = SPClaimProviderManager.Local;
//создание утверждений из идентификатора, чтобы можно было видеть,
//что исходный поставщик пришел из Windows
SPClaim idClaim = cpm.ConvertIdentifierToClaim(oldUser,
SPIdentifierTypes.EncodedClaim);
//это пользователь утверждений Windows, и мы собираемся
//преобразовать его в пользователя утверждений SAML
if (idClaim.OriginalIssuer == "Windows")
{
//пользователь утверждений Windows имеет формат домен\пользователь;
//группы утверждений windows имеют формат ИД безопасности
if (idClaim.Value.Contains("\\"))
{
//миграция пользователя
//здесь можно проверить удостоверение пользователя
//могут иметься некоторые учетные записи с утверждениями Windows, которые
//не требуется пока преобразовывать, а также могут быть учетные записи службы, которые
//передаются, но которые не требуется преобразовывать;
//в идеале можно было бы просто прочитать источник данных, чтобы определить,
//каких пользователей следует преобразовать, а затем проверить из удостоверения
//здесь проверяется, имеются ли пользователи, которых следует
//преобразовать
//в этом случае я преобразую только одного пользователя - с именем darrins
if (idClaim.Value == "contoso\\darrins")
{
// здесь я получаю идентификационное утверждение, захватывая
//часть, расположенную после "домен\" и добавляя к ней суффикс
//электронной почты, после чего получаем darrins@contoso.com
SPClaim migratedUserClaim =
SPClaimProviderManager.CreateUserClaim(
idClaim.Value.Split('\\')[1] + "@contoso.com",
SPOriginalIssuerType.TrustedProvider,
SPTrustedIdentityTokenIssuerName);
//получение кодированного значения, соответствующего новому
//идентификационному утверждению
value = migratedUserClaim.ToEncodedString();
}
}
else
{
//миграция группы
//получение простого имени группы
SecurityIdentifier sid =
new SecurityIdentifier(idClaim.Value);
NTAccount groupAccount =
(NTAccount)sid.Translate(typeof(NTAccount));
string groupName = groupAccount.ToString();
//следует выполнить миграцию только группы Portal People
if (groupName.ToLower() == "contoso\\portal people")
{
//создание нового утверждения роли
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));
//получение кодированного значения, соответствующего новому утверждению роли
value = migratedGroupClaim.ToEncodedString();
}
}
}
break;
case SPWebApplication.AuthenticationMethod.Forms:
//код для преобразования из форм располагается здесь
Debug.WriteLine(oldUser);
break;
}
}
catch (Exception ex)
{
Debug.WriteLine(ex.Message);
}
return value;
}
}
}
Первое, что я делаю — проверяю значение переданного параметра SPWebApplication.AuthenticationMethod Так как я хочу преобразовать только пользователей с утверждениями Windows (утверждения Windows в утверждения SAML), это единственная ситуация, в которой должен выполняться код Если текущий пользователь — пользователь с утверждениями, я получаю ссылку на локальный объект SPClaimProviderManager для извлечения представления утверждения пользователя. Это нужно, чтобы определить, применяет ли пользователь утверждения Windows, FBA или SAML. В этом случае я хочу преобразовать только пользователей с утверждениями Windows.
После определения типа пользователя мне нужно узнать, применяется ли утверждение для пользователя или группы. Вот одна из странностей, которую можно заметить. Даже если текущий пользователь — это группа утверждений Windows, параметр isGroup, передаваемый методу, возвращает значение "false". Это значит, что нужно определить, является ли текущая "сущность" пользователем или группой. Поэтому я просто смотрю значение утверждения — если это пользователь, то значение будет представлено в формате домен\пользователь; в противном случае это группа, представленная в формате ИД безопасности.
Зная тип сущности, можно определить необходимый тип утверждения. Для пользователя нужно создать утверждение удостоверения. Один из параметров, который требуется знать, — это SPTrustedIdentityTokenIssuer, используемый веб-приложением. Можно было бы написать код для этой цели, но вместо этого я пошел по "ленивому" пути (все-таки это пример) и осуществляю принудительную передачу правильного имени в конструкторе для моего класса. Итак, я беру имя для входа пользователя (после доменной части), и в этом примере адрес электронной почты всегда будет указываться как имя_для_входа@contoso.com. Если в вашей организации используется другой способ задания адреса электронной почты, потребуется использовать другие средства для проверки правильности этого адреса. Я использую эту процедуру вместе с примером кода, описанным выше, для создания утверждения удостоверения для данного пользователя. Это значение и возвращается; в этом случае в него преобразуется учетная запись vbtoys\darrins. (надеюсь, литературно грамотные пользователи не будут досаждать меня по поводу конструкции этого предложения)
Для групп я беру переданный ИД безопасности и использую класс NTAccount для получения понятного имени группы. Его я использую для создания нового утверждения роли, а затем получаю кодированное значение, к которому должна перейти группа. (как там теперь с грамматикой — вроде, получше?!?)
Еще один момент, который следует отметить, — для групп и пользователей я не пытаюсь автоматически выполнить перенос всех данных. Есть кое-какие элементы, которые вы, возможно, не захотите переносить, например учетные записи службы, встроенные учетные записи и т. д.; их перенос будет зависеть от ваших требований. Удобство данного метода миграции заключается в том, что ее можно выполнять столько раз, сколько нужно. Можно перенести подмножество пользователей, выполнить частичную или полную миграцию. Допустим, что есть база данных со всеми пользователями, данные которых нужно перенести. Можно запросить базу данных для получения списка и затем по мере вызова каждого пользователя в коде миграции можно проверять, есть этот пользователь в списке, полученном из базы данных. Это всего один пример.
Итак, я не хочу вдаваться в подробности документации пакета SDK, над которой Брайан и Раджу так хорошо потрудились, но я чувствую себя обязанным по крайней мере рассказать, как вызывается класс, чтобы это не вызвало у вас проблем. Я написал приложение Winforms и добавил ссылку на проект в настраиваемую сборку, описанную выше. Это значительно облегчает совместное построение и отладку. Код, используемый для вызова класса и выполнения миграции, выглядит следующим образом:
//получение ссылки на мое веб-приложение
SPWebApplication wa = SPWebApplication.Lookup(new Uri("https://foo"));
//это имя моего доверенного поставщика маркера удостоверения
string SPTrustedIdentityTokenIssuerName = "ADFSProvider";
//создание экземпляра настраиваемого класса миграции обратного вызова пользователя
MigrateUserSample.MigrateTest mt =
new MigrateUserSample.MigrateTest(SPTrustedIdentityTokenIssuerName);
//создание ссылки интерфейса на него
IMigrateUserCallback muc = mt as IMigrateUserCallback;
//миграция пользователей
wa.MigrateUsers(muc);
Вот и все. Нужно учесть множество других вариантов для охвата всех возможных ситуаций, но для начала это неплохо, а Брайан и Раджу внесут свою лепту в эту работу.
Это локализованная запись блога. Исходная статья находится по адресу Migrating User Accounts from Windows Claims to SAML Claims