Создаем ассоциированные с сайтом группы в Sharepoint программно

Если вы создаете сайт в Sharepoint и при этом используете уникальные настройки безопасноти (Use unique permissions), то после того как выбранный шаблон будет примен к только что созданному сайту, вы будете перенаправлены на стандартную страницу с настройками групп для нового сайта (Set Up Groups for this Site” _layouts/permsetup.aspx):

Используя данную страницу вы можете создать 3 стандартных группы: Посетители, Участники и Владельцы (Visitors, Members, Owners). Возникает вопрос — как создать эти группы программно? Практически это может пригодится во многих сценариях — например при создании сайта, используя кастомный шаблон и кастомную страницу создания (а не стандартную страницу создания сайтов в Sharepoint – _layouts/create.aspx). При этом мы можем избавить администратора от одного шага, который необходимо выполнить вручную, создав указанные группы автоматически.

Эти группы, в отличие от обычных пользовательских групп (SPWeb.SiteGroups), ассоциированны (associated) с данным сайтом и выделены в объектной модели в 3 отдельные свойства класса SPWeb: AssociatedOwnerGroup, AssociatedMemberGroup и AssociatedVisitorGroup. Прежде всего, нам необходимо знать, какие права назначаются этим группам на сайте. Они перечисленны в следующей таблице (я также использую термины объектной модели, а не отображаемые названия):

Группа Права
AssociatedOwnerGroup SPRoleType.Administrator
AssociatedMemberGroup SPRoleType.Contributor
AssociatedVisitorGroup SPRoleType.Reader

Используя эту таблицу, мы можем создать ассоциированные группы программно:

private void setupSecurity(SPWeb web)
{
    // create groups
    string ownersName = SPResource.GetString("DefaultOwnerGroupName",
new object[] {web.Title});
    var owners = SecurityHelper.CreateSiteGroup(web, ownersName);
    owners.Owner = owners;
    owners.Update();
    string visitorsName = SPResource.GetString("DefaultVisitorGroupName",
new object[] {web.Title});
    var visitors = SecurityHelper.CreateSiteGroup(web, visitorsName);
    visitors.Owner = owners;
    visitors.Update();
    string membersName = SPResource.GetString("DefaultMemberGroupName",
new object[] {web.Title});
    var members = SecurityHelper.CreateSiteGroup(web, membersName);
    members.Owner = owners;
    members.Update();
    // assign permissions
    SecurityHelper.AssignGroupRoleToSecurableObject(web, web,
SPRoleType.Reader, visitors);
    SecurityHelper.AssignGroupRoleToSecurableObject(web, web,
SPRoleType.Contributor, members);
    SecurityHelper.AssignGroupRoleToSecurableObject(web, web,
SPRoleType.Administrator, owners);
    // associate
    web.AssociatedOwnerGroup = web.SiteGroups[ownersName];
    web.Update();
    web.AssociatedMemberGroup = web.SiteGroups[membersName];
    web.Update();
    web.AssociatedVisitorGroup = web.SiteGroups[visitorsName];
    web.Update();
}

Рассмотрим код подробнее. Прежде всего нам необходимо получить названия групп. Чтобы сделать это, используем такой же подход, что и на стандартной странице permsetup.aspx (вы можете просмотреть codebehind любой стандартной layout-ной страницы с помощью Reflector-а. Естественно, если этот код не деобфусцирован):

string ownersName = SPResource.GetString("DefaultOwnerGroupName",
    new object[] {web.Title});

Также, если мы хотим, чтобы наш код был как можно более похож на стандартный (с точки зрения конечного результата), нужно установить владельца (свойство Owner класса SPGroup) для создаваемых групп (владельца группы вы можете просмотреть на странице просмотра деталей группы). При этом AssociatedOwnerGroup будет владельцем самой себя.

После того, как группы созданы, нужно назначить им права на сайте, используя приведенную выше таблицу. Это делается в следующих строках:

// assign permissions
SecurityHelper.AssignGroupRoleToSecurableObject(web, web,
    SPRoleType.Reader, visitors);
SecurityHelper.AssignGroupRoleToSecurableObject(web, web,
    SPRoleType.Contributor, members);
SecurityHelper.AssignGroupRoleToSecurableObject(web, web,
    SPRoleType.Administrator, owners);

В указанных примерах я использовал небольшой вспомогательный класс SecurityHelper, который может пригодится не только в контексте рассматриваемой темы. Приведу его код:

public static class SecurityHelper
{
    public static SPGroup CreateSiteGroup(SPWeb web, string groupName)
    {
        if (isGroupExist(web, groupName))
        {
            throw new Exception(string.Format("Group '{0}' already exists",
                groupName));
        }
        web.SiteGroups.Add(groupName, web.SiteAdministrators[0], null,
            string.Empty);
        return web.SiteGroups[groupName];
    }
    private static bool isGroupExist(SPWeb web, string groupName)
    {
        return web.SiteGroups.Cast<SPGroup>().Any(g =>
            string.Compare(g.Name, groupName, true) == 0);
    }
    public static void AssignGroupRoleToSecurableObject(SPWeb web,
        ISecurableObject securableObject, SPRoleType roleType, SPGroup group)
    {
        SPRoleAssignment roleAssignment = new SPRoleAssignment(group);
        SPRoleDefinition roleDefinition = web.RoleDefinitions.GetByType(roleType);
        assignRoleToSecurableObject(securableObject, roleDefinition, roleAssignment, true);
    }
    public static void AssignGroupRoleToSecurableObject(SPWeb web,
        ISecurableObject securableObject, SPRoleType roleType, SPGroup group,
        bool copyRoleAssignment)
    {
        SPRoleAssignment roleAssignment = new SPRoleAssignment(group);
        SPRoleDefinition roleDefinition = web.RoleDefinitions.GetByType(roleType);
        assignRoleToSecurableObject(securableObject, roleDefinition, roleAssignment,
            copyRoleAssignment);
    }
    private static void assignRoleToSecurableObject(ISecurableObject securableObject,
        SPRoleDefinition roleDefinition, SPRoleAssignment roleAssignment,
        bool copyRoleAssignment)
    {
        roleAssignment.RoleDefinitionBindings.Add(roleDefinition);
        if (!securableObject.HasUniqueRoleAssignments)
        {
            securableObject.BreakRoleInheritance(copyRoleAssignment);
        }
        securableObject.RoleAssignments.Add(roleAssignment);
    }
}

И последний шаг – ассоциируем созданные группы с сайтом:

// associate
web.AssociatedOwnerGroup = web.SiteGroups[ownersName];
web.Update();
web.AssociatedMemberGroup = web.SiteGroups[membersName];
web.Update();
web.AssociatedVisitorGroup = web.SiteGroups[visitorsName];
web.Update();

Остается только определить, в какой момент вызвать рассмотренный метод setupSecurity(…) в кастомизированном процессе создания сайта. Одно из очевидных мест — feature receiver одной из фич, которые активируются на нашем сайте автоматически (фичи, которые присутствуют в файле onet.xml с кастомным шаблоном сайта). К сожалению, если вы создаете кастомный портал, чей шаблон основан на стандартном шаблоне публикации (OTB publishing site template), то feature receiver – не подходящее место, потому что Sharepoint перезапишет ассоциированные группы на более поздних этапах конфигурации портала при его создании (во всех 3-х свойствах объекта класса SPWeb будет null, кроме AssociatedVisitorGroup). Для указанного случая я нашел подходящее место для создания и настройки ассоциированных групп – в кастомном portal provisioning provider-е:

public class CondoSitePortalProvisioningProvider : SPWebProvisioningProvider
{
    public override void Provision(SPWebProvisioningProperties props)
    {
        // PortalProvisioningProvider is sealed so aggregate it instead of inheritance
        var publishingPortalProvisioningProvider =
            new PortalProvisioningProvider();
        publishingPortalProvisioningProvider.Provision(props);
        this.setupSecurity(props.Web);
    }
    ...
}

Но с этим подходом есть известная проблема: код будет исполнятся, используя язык сайта, в контексте которого процесс создания нового сайта был инициирован, т.е. не в контексте создаваемого сайта. Это означает, что если вы, например, открыли вашу кастомную страницу создания сайта в английском сайте и создаете русский подсайт, то названия ассоциированных груп все равно будут на английском (Owners, Members, Visitors). Для того, чтобы получить названия групп на языке создаваемого сайта, мы должны временно сменить локаль текущего потока. В одном из своих постов (на английском) я показывал как это можно сделать:

var currentCulture = Thread.CurrentThread.CurrentCulture;
var currentUICulture = Thread.CurrentThread.CurrentUICulture;
try
{
    SPUtility.SetThreadCulture(web);
    setupSecurity(web);
}
finally
{
    Thread.CurrentThread.CurrentCulture = currentCulture;
    Thread.CurrentThread.CurrentUICulture = currentUICulture;
}

Теперь если вы создадите сайт на русском языке, ассоциированные группы будут иметь русские названия: Владельцы, Участники, Посетители. Таким образом задача создания ассоциированных групп программно будет полностью решена.

Реклама

Об авторе sadomovalex

Старший инженер, team lead, консультант. Работаю в стеке .Net. Последние несколько лет занимаюсь разработкой enterprise приложений под Sharepoint, чему и будет в основном посвящена тематика этого блога. Также активно использую и интересуюсь ASP.Net MVC, DDD, TDD, Agile. Активно участвую в жизни многих профессиональных сообществ, SPb .Net UG, SPb ALT.Net, rsdn, Finland SP UG и др.
Запись опубликована в рубрике Security, Sharepoint. Добавьте в закладки постоянную ссылку.

Добавить комментарий

Заполните поля или щелкните по значку, чтобы оставить свой комментарий:

Логотип WordPress.com

Для комментария используется ваша учётная запись WordPress.com. Выход / Изменить )

Фотография Twitter

Для комментария используется ваша учётная запись Twitter. Выход / Изменить )

Фотография Facebook

Для комментария используется ваша учётная запись Facebook. Выход / Изменить )

Google+ photo

Для комментария используется ваша учётная запись Google+. Выход / Изменить )

Connecting to %s