В Microsoft SharePoint 2010 была введена новая модель идентификации на основе заявок (claims-based identity model) для создания приложений с поддержкой заявок. Когда пользователь входит в такое приложение, он предоставляет этому приложению идентификацию в виде набора заявок. Эти заявки обеспечивают более тонкую авторизацию для доступа к контенту. Таким образом, пользователи с определенными атрибутами, такими как Country Code и Department Code, получают доступ к соответствующему контенту.
Реализация защиты на основе заявок на уровне предприятия ставит некоторые интересные и трудные задачи. Мы исследуем процесс создания провайдера настраиваемых заявок (custom claims provider, CCP) в SharePoint, интегрируем его с Microsoft FAST Search Server 2010 for SharePoint, рассмотрим управление контентом, контролируемым заявками, с точки зрения пользователя и обсудим, как избежать некоторых подвохов. Попутно вы узнаете, как проектировать и реализовать динамическую модель защиты на основе настраиваемых заявок в SharePoint 2010. В нашем примере мы задействуем портал для управления кадрами ContentWeb, принадлежащий вымышленной компании Contoso.
Обзор приложения
Contoso — глобальная компания со штатом почти в 100 000 сотрудников, и ContentWeb хранит море информации, которой авторы контента должны управлять как ежедневно, так и еженедельно. В большинстве случаев какой-то контент применим к персоналу только в одной стране, только к одной специальности или ко всем сотрудникам Contoso. Однако часть контента является конфиденциальными данными, которые должны быть доступны лишь малому подмножеству сотрудников (например, вице-президентам).
Чтобы охватить столь широкую аудиторию, ContentWeb использует SharePoint Groups (SPG) с настраиваемыми заявками для явной защиты контента. Заявки, специфичные для требований Contoso, хранятся в базе данных и применяются для определения «набора» заявок пользователя. Именно этот набор задает, что может видеть сотрудник и чего не может. Административный интерфейс позволяет сотрудникам, работающим с информацией, которые поддерживают портал, изменять или дополнить свои наборы заявок, что дает им возможность просматривать портал в качестве пользователей с разными правами доступа. Эта мощная функция называется View Portal As (VPA).
ContentWeb использует аутентификацию Windows на основе заявок в SharePoint 2010, а FAST Search Server 2010 for SharePoint — в качестве поисковой системы в федеративной корпоративной среде.
Зачем нужен CCP?
Когда пользователь входит в ContentWeb, процесс аутентификации Windows на основе заявок предоставляет набор заявок из Active Directory, которая выступает в роли провайдера идентификации. Однако сами по себе эти заявки недостаточны для того, чтобы ContentWeb защищал контент и разграничивал к нему доступ. ContentWeb требует дополнительной информации о пользователе из корпоративных репозитариев данных, например из SAP, чтобы сформировать полный набор заявок, описывающих этого пользователя. Таким образом, для дополнения заявок нужен CCP. Мы используем эти настраиваемые заявки для явной защиты контента и определения прав доступа конкретного пользователя.
Наличие CCP позволяет Contoso определять, какие типы и значения заявок нужно использовать для описания сотрудников, поставщиков и других сторон, с которыми работает эта компания. Данная модель является масштабируемой; она обеспечивает для Contoso возможность эффективной адаптации по мере изменения ее бизнес-требований.
Поскольку редактировать заявки, исходящие из Active Directory, не так-то просто, CCP понадобится и для поддержки VPA. VPA в основном используется, когда авторизованное лицо (пользователь A) хочет увидеть портал как другое лицо (пользователь B). В таких случаях провайдер настраиваемых заявок добавляет настраиваемые заявки пользователя B в набор заявок пользователя User A и тем самым авторизует пользователя A на просмотр контента, к которому может обращаться пользователь B.
Принадлежащие пользователю заявки являются атрибутами, которые представляют его «профиль». У пользователей могут быть как простые, так и составные (compound) заявки. Простые заявки, например CountryCode и UserType, понять легко. ContentWeb использует предопределенный набор простых заявок, или точнее, простых типов заявок. Однако иногда сотруднику нужно выдавать разрешение на доступ к контенту, имеющему более одного типа заявки. Например, CountryCode=US AND UserType=FullTimeEmployee — здесь условие AND создает составные заявки.
Изначально SharePoint 2010 оценивает заявки в наборе заявок пользователя, применяя только логику OR. Это означает, что SharePoint 2010 поддерживает лишь условие OR для безопасности и разграничения доступа, а этого не всегда достаточно. Давайте рассмотрим пример. В ContentWeb имеется как заявка CountryCode, так и заявка UserType. Некоторый контент для сотрудников в США, занятых полный рабочий день, нужно защищать, используя CountryCode = US AND UserType = FullTimeEmployee. Мы не можем удовлетворить это требование непосредственно в SharePoint 2010 с помощью простых заявок CountryCode или UserType, поэтому мы должны создать составную заявку, представляющую обе простые заявки.
Мы могли бы создать новый тип заявки для сотрудников в США, занятых полный рабочий день. Однако, если компания откроет новые офисы в других странах, нам придется обновлять исходный код нашего CCP, чтобы добавить соответствующие типы заявок. Очевидно, что этот подход может оказаться дорогостоящим и требующим уймы времени. Более того, бизнес-группа ContentWeb хочет иметь возможность добавлять и удалять составные типы заявок, не выпуская каждый раз новое приложение. Как видите, ContentWeb требуется динамическая и расширяемая модель защиты.
Как мы будем управлять всеми данными заявок? Мы воспользуемся базой данных с именем ContentWebAdminDB для хранения метаданных заявок. ContentWebAdminDB является серверным источником заявок; она консолидирует данных ото всех источников заявок из SAP и других корпоративных баз данных. (Подробнее об этой базе данных мы поговорим чуть позже.) И еще мы используем обычное административное приложение ASP.NET под названием ContentWebAdmin для подключения к этой базе данных с целью создания и обновления метаданных составных заявок и конфигурации VPA. (Подробное описание этого приложения выходит за рамки нашей статьи.) Составные заявки создаются на основе простых. Так, в приложении ContentWebAdmin можно создать составную заявку US-FullTimeEmployee, используя простые заявки CountryCode = US и UserType = FullTimeEmployee. Благодаря метаданным этой составной заявки в набор заявок любого пользователя, имеющего простые заявки CountryCode = US и UserType = FullTimeEmployee, будет добавлена составная заявка US-FullTimeEmployee = true. Заметьте, что ни в одном наборе заявок не появится составной заявки US-FullTimeEmployee = false. При такой динамической архитектуре составных заявок у каждого пользователя есть всего несколько десятков заявок в целом, хотя число комбинаций составных заявок может оказаться весьма высоким. Это ключ к хорошей производительности.
Когда пользователь входит в ContentWeb, вызывается CCP. Тот в свою очередь вызывает собственную хранимую процедуру, sp_GetUserClaims, в ContentWebAdminDB и передает заявку идентификации пользователя. Хранимая процедура проверяет конфигурацию VPA и возвращает все простые и составные заявки для данного пользователя в CCP. Как видите, динамические составные заявки — отнюдь не то же самое, что динамические правила авторизации. Наше решение на основе составных заявок не изменяет то, как SharePoint 2010 оценивает заявки. Мы просто вводим составные заявки в набор заявок пользователя, основываясь на конфигурации метаданных составных заявок. Высокоуровневая схема ContentWeb показана на рис. 1.
Увеличить
Рис. 1. Высокоуровневая схема ContentWeb
ContentWeb CCP изнутри
В ContentWeb CCP есть особый класс, наследующий от Microsoft.SharePoint.Administration.SPClaimProvider. Часть этого класса показана на рис. 2. Обратите внимание на выделенную строку кода. Мы добавили ее, чтобы было проще отображать типы и значения наших настраиваемых заявок более наглядным образом.
Рис. 2. Отображение заявок в более читаемом виде
protected override void FillResolve(
Uri context, string[] entityTypes, SPClaim resolveInput,
List<Microsoft.SharePoint.WebControls.PickerEntity> resolved)
{
string claimTypeName = string.Empty;
string claimValue = string.Empty;
// Обрабатывает только типы заявок ContentWeb
if (null != resolveInput && resolveInput.ClaimType.Contains(
ContentWebClaimTypes.ContentWebClaimTypePrefix))
{
claimValue = resolveInput.Value.ToLower();
claimTypeName = GetName(resolveInput.ClaimType);
PickerEntity entity = CreatePickerEntity();
entity.Claim = resolveInput;
entity.Description =
resolveInput.ClaimType + " = " + claimValue;
entity.DisplayText = claimTypeName + " = " + claimValue;
entity.EntityData[PeopleEditorEntityDataKeys.DisplayName] =
claimTypeName + " = " + claimValue;
entity.EntityType = SPClaimEntityTypes.User;
entity.IsResolved = true;
resolved.Add(entity);
}
}
ContentWeb CCP требуется строка подключения для вызова хранимой процедуры в ContentWebAdminDB, чтобы получить заявки пользователей. Поскольку CCP — функция уровня фермы и не находится в рамках какого-либо веб-приложения, мы не можем использовать файл web.config для конфигурирования строки подключения к базе данных. Можно было бы задействовать machine.config, но этот вариант не идеален, потому что у нас множество веб-клиентов. Вместо этого мы применим иерархическое объектное хранилище SharePoint — SPPersistedObject, где и будем хранить строку подключения к базе данных. Благодаря этому строка подключения будет доступна всем веб-клиентам в ферме SharePoint.
В ContentWeb CCP мы создаем класс, наследующий от Microsoft.SharePoint.Administration.SPPersistedObject, как показано на рис. 3.
Рис. 3. Класс ConnectionStringStorage
[Guid("56705e15-abd3-44f0-adea-91488da1a572")]
public class ConnectionStringStorage
: Microsoft.SharePoint.Administration.SPPersistedObject
{
[Persisted]
private string m_connectionString;
public ConnectionStringStorage()
{
}
public ConnectionStringStorage(
string name, SPPersistedObject parent)
: base(name, parent)
{
}
public ConnectionStringStorage(
string name, SPPersistedObject parent, Guid guid)
: base(name, parent, guid)
{
}
public string ConnectionString
{
get { return m_connectionString; }
set { m_connectionString = value; }
}
}
Мы используем следующий скрипт Windows PowerShell для регистрации строки подключения к базе данных:
[System.Reflection.Assembly]::LoadWithPartialName(
"Microsoft.Sample.ContentWeb.ClaimsSecurity")
$id = new-object System.Guid(
"56705e15-abd3-44f0-adea-91488da1a572")
$farm = Get-SPFarm
$existingObject = $farm.GetObject($id)
$existingObject.Delete()
$newObject = new-object Microsoft.Sample.Contoso.
ClaimsSecurity.ConnectionStringStorage(
"ConnectionString", $farm, $id)
$newObject.ConnectionString = "Data Source=
ContentWebAdminSQLServer;Initial Catalog=
ContentWebAdminDB;Integrated Security=True;Timeout=30"
$newObject.Update();
Iisreset
Как видите, мы ссылаемся на dll-файл CCP. Мы создаем новый объект ConnectionStringStorage и задаем его свойство ConnectionString. Наконец, вызываем его метод Update для сохранения значения этого свойства в базе данных конфигурации SharePoint.
Мы рекомендуем, чтобы у строки подключения значение тайм-аута было менее 60 секунд. В CCP по умолчанию на время ожидания выполнения отводится 60 секунд. То есть, если время ожидания соединения с базой данных истечет, у вас будет возможность получить реальное исключение из-за проблемы со строкой подключения вместо вводящего в заблуждение исключения, возникающего при выполнении CCP.
В конструкторе CCP мы получаем строку подключения и сохраняем ее в переменной уровня модуля:
public class ContentWebClaimProvider : SPClaimProvider
{
private static string connectionString = null;
public ContentWebClaimProvider(string displayName)
: base(displayName)
{
Guid guid = new Guid(
@"56705e15-abd3-44f0-adea-91488da1a572");
ConnectionStringStorage storage =
(ConnectionStringStorage)SPFarm.Local.GetObject(guid);
connectionString = storage.ConnectionString;
}
}
Нам нужна возможность отслеживать, когда CCP подключается к базе данных для получения заявок пользователя. В SharePoint 2010 есть новый компонент, который отлично подходит для этой цели. Это объект Microsoft.SharePoint.Utilities.SPMonitoredScope, и он ведет мониторинг производительности указанного блока кода и использования им ресурсов. Следующий код протоколирует вызовы CCP, адресованные базе данных:
using (new SPMonitoredScope("ContentWeb.CCP.GetUserClaims", 5))
{
userClaimsDataSet = CustomClaimSourceDA.GetUserClaims(
userAlias, connectionString);
}
Кроме того, можно настроить уровни детализации протоколирования в SharePoint Central Administration. Они контролируются объектом Monitoring в категории SharePoint Foundation в разделе Monitoring | Reporting | Configure diagnostic logging.
База данных ContentWebAdminDB изнутри
Как уже отмечалось, ContentWebAdminDB консолидирует данные заявок пользователя из SAP и других корпоративных баз данных и содержит источник настраиваемых заявок. ContentWeb CCP вызывает хранимую процедуру, sp_GetUserClaims, для получения настраиваемых заявок пользователя.
Приложение ContentWebAdmin позволяет авторизованным пользователям конфигурировать составные заявки и VPA для конкретных пользователей. ContentWebAdminDB хранит конфигурационные метаданные составных заявок и конфигурацию VPA в таблицах. Когда ContentWeb CCP вызывает sp_GetUserClaims, эта хранимая процедура проверяет конфигурацию VPA, а затем возвращает как простые, так и составные заявки. Детальное рассмотрение устройства и реализации этой базы данных выходит за рамки этой статьи. Однако мы включили SQL-скрипт для примера хранимой процедуры sp_GetUserClaims с «зашитыми» в код значениями заявок, и вы можете скачать его вместе со всем пакетом кода.
Использование групп SharePoint
Группа SharePoint (SharePoint Group, SPG) — логическая оболочка пользователей SharePoint. Это удобно для ContentWeb, где используются сотни составных заявок. Без SPG авторам контента было бы очень трудно управлять всеми заявками.
Использование SPG-групп дает следующие преимущества.
- Понятное именование SPG дают возможность пользователям формировать понятные имена групп для не слишком вразумительных значений заявок. Например, в ContentWeb есть SPG с названием «US FullTime Employees», которая описывает пользователей, имеющих значения заявок Country=United States, CompanyCode=1010 и UserSubType=FTE.
- Поддержка разрешения имен People Picker Элемент управления People Picker в SharePoint 2010 имеет встроенную поддержку разрешения имен для SPG. Это облегчает поиск и выбор SPG-групп, как показано на рис. 4.
Рис. 4. Группы SharePoint в People Picker
Рис. 5. Пример конфигурационного XML-файла
<?xml version="1.0" encoding="utf-8"?>
<SharePointGroups url="http://contentweb"
owner="contoso\ivfeng" >
<SharePointGroup name="ContentWeb-FTE"
description="ContentWeb-FTE" permissionLevel="Read">
<Claim type="UserType" value="emp"/>
</SharePointGroup>
<SharePointGroup name="ContentWeb_US_0000_Emp_Corp_HRPro"
description="ContentWeb_US_0000_Emp_Corp_HRPro">
<Claim type="CompoundClaim" value="US+emp+corp+hrpro"/>
</SharePointGroup>
<SharePointGroup name="ContentWeb_US_0000_Emp_Corp_Manager"
description="ContentWeb_US_0000_Emp_Corp_Manager">
<Claim type="CompoundClaim" value="us+emp+corp+manager"/>
<Claim type="CompoundClaim" value="US+emp+corp+hrpro"/>
</SharePointGroup>
</SharePointGroups>
Использование SPG для разграничения доступа
Чтобы задействовать разграничение доступа (targeting), вам потребуется сервисное приложение User Profile с включенной поддержкой веб-приложения.
При использовании SPG для разграничения доступа следует остерегаться одного хитрого подвоха. Изначально мы обнаружили, что SPG-группы, содержащие пользователей или группы безопасности Active Directory, работают нормально, а SPG с настраиваемыми заявками пользователей вообще не работают. Как оказалось, причина связана с уровнем разрешений этих групп. Создавая SPG с настраиваемыми заявками, мы не хотели присваивать уровень разрешений по умолчанию. Однако, если SPG не назначен уровень разрешений при ее создании и впоследствии она используется для целей защиты, она будет помечена флагом особого уровня разрешений — Limited Access (ограниченный доступ), как показано на рис. 6
Рис. 6. Группа SharePoint с ограниченным доступом
Поскольку в ContentWeb сотни SPG и большинство из них используется только для целевых аудиторий, им не был присвоен уровень разрешений по умолчанию. Но обработчик целевой аудитории не может извлекать данные из таких SPG без какого-либо уровня разрешений. К счастью, мы нашли обходной путь через UI разрешений страницы для целевой аудитории. Мы выдавали разрешение Read группам, используемым для целевой аудитории, а затем немедленно отменяли это разрешение. Эта операция заставляла SharePoint помечать соответствующие группы как имеющие уровень разрешений Limited Access, и целевая аудитория тоже получала этот уровень. Важно отметить, что вам не требуется многократно выполнять эту операцию выдачи и отмены разрешения для SPG. Достаточно сделать это лишь раз в дереве наследования атрибутов защиты. Будьте осторожны: если вы нарушите наследование атрибутов защиты, вам придется делать это снова, даже если вы выполняли это на родительском уровне.
Реализация поиска для ContentWeb
Одной из наших основных целей в этом приложении было улучшение удобства поиска для сотрудников Contoso. Мы хотели, чтобы в ContentWeb создавали контент, который гораздо проще искать, чем это было раньше.
Чтобы добиться этого для ContentWeb, мы использовали FAST Search Server 2010 for SharePoint, размещенный в федеративной среде. Чтобы увидеть федеративную среду FAST Search в Contoso, вернитесь к рис. 1. На нем показана ферма SharePoint под названием FAST Search Federation Farm. Поддерживается эта ферма ядром и базой данных Microsoft FAST Search Server for SharePoint. Ферма FAST Search содержит все Query Search Service Applications (Query SSA) и Content SSA.
Естественно, мы создали источник контента в существующем Content SSA. (Для FAST Search Server рекомендуется только одно Content SSA.) После этого Content SSA будет индексировать базу данных контента ContentWeb и сделает поисковый индекс доступным ContentWeb и другим приложениям интрасети.
Затем мы создали отдельное Query SSA и опубликовали его в ContentWeb. При тесте начального поиска в ContentWeb мы обнаружили, что базовый поиск работает, но функция исключения из поиска защищенного контента (search security trimming) работала не так, как ожидалось. В случае любых страниц, защищенных настраиваемыми заявками ContentWeb, пользователи не могли видеть ссылки в результатах поиска, даже если у них были необходимые разрешения. Наконец, мы выяснили, что сопоставления типов настраиваемых заявок ContentWeb должны быть зарегистрированы на ферме FAST Search SharePoint, — только тогда функция исключения из поиска защищенного контента работает корректно. Самый простой способ сделать это — развернуть CCP на ферме FAST Search SharePoint. Нам пришлось не только позаботиться о том, что сопоставления типов заявок зарегистрированы, но и убедиться, что все идентификаторы заявок (показаны в поле Account на рис. 7) одинаковы в рамках всех ферм SharePoint для одной и той же пары «тип-значение» заявки.
Рис. 7. Идентификаторы заявок
В настоящее время — даже после кумулятивных обновлений SharePoint 2010 за июнь 2011 г. — сопоставления типов заявок в SharePoint 2010 являются неизменяемыми. Как только вы развертываете CCP на ферме SharePoint 2010, сопоставления типов заявок регистрируются и остаются неизменными, даже если вы удаляете и заново развертываете CCP. Поскольку у вас могут быть CCP на нескольких фермах SharePoint, очень важно развертывать все CCP в одинаковой последовательности, чтобы идентификаторы заявок были одинаковыми на всех фермах. Если вы этого не сделаете, идентификаторы последовательности не будут выровнены и поиск будет возвращать неточные результаты.
При индексации контента ContentWeb в процессе поиска CCP не вызывается. Достаточно зарегистрировать сопоставления типов заявок на ферме FAST Search, чтобы поисковая система могла декодировать настраиваемые заявки ContentWeb. После этого она сохраняет декодированные настраиваемые заявки в поисковом индексе для безопасности. Удаление ContentWeb CCP не обязательно, так как сопоставления типов заявок все равно останутся. Более того, на деле нам не понадобился ContentWeb CCP для дополнения пользовательских заявок на ферме FAST Search.
Результаты поиска с исключением защищенного контента: порталы Contoso в интрасети
Чтобы контент ContentWeb можно было искать с других порталов Contoso в интрасети, нужно обеспечить следующее:
- все порталы в интрасети, которые должны видеть контент ContentWeb, нужно создавать на основе SharePoint 2010, и это должны быть веб-приложения на основе заявок, использующие аутентификацию средствами Windows;
- все порталы в интрасети должны запрашивать один и тот же индекс FAST Search;
- на всех порталах в интрасети нужно установить ContentWeb CCP. (Если у вас более одного CCP, они должны устанавливаться в одинаковой последовательности, чтобы гарантировать тождество идентификаторов заявок.)
Если все эти условия соблюдены, контент ContentWeb можно будет искать на всех порталах Contoso в интрасети.
Сопоставления типов настраиваемых заявок должны быть зарегистрированы на ферме FAST Search SharePoint. |
Заключение
SharePoint 2010 обеспечивает тесную интеграцию с системой безопасности на основе заявок. Группы SharePoint, заявки и провайдер настраиваемых заявок позволяют авторизовать контент как для доступа всех сотрудников предприятия, так и определенных групп. Как вы убедились, многие сценарии можно реализовать относительно легко.
Эта архитектура также позволяет разработчикам создавать надежные инструменты, с помощью которых информационные работники (например, персонал техподдержки) смогут изменять свои заявки и просматривать сайт от лица другого пользователя, а затем возвращаться к своим правам доступа.
Как и в большинстве случаев реализаций новой архитектуры, мы не обошлись без проблем, которые пришлось решать самостоятельно, но конечный результат того стоил. Предоставленные нами примеры кода должны помочь вам выбрать верное направление и успешно приступить к развертыванию корпоративного портала нового поколения!