Есть много разных мнений, когда речь заходит о Host Web и App Web. Многие люди считают, что для полной изоляции от SharePoint «из коробки» все данные приложения должны храниться и управляться в App Web. Это устраняет необходимость сложных разрешений приложений на хост-сайте и риск отказа пользователей, которые не хотят устанавливать приложение из-за них. Однако по моему опыту недостатком этого является то, что по умолчанию App Web полностью наследует свои разрешения от Host Web. Это означает, что пользователям с разрешениями на чтение в Host Web не разрешено создавать, обновлять или удалять данные, хранящиеся в списках в App Web. В некоторых сценариях вы хотите, чтобы даже пользователи, которые являются просто посетителями в Host Web, работали с вашим приложением и, следовательно, могли обновлять данные, хранящиеся в App Web. Что делать?

Настройка разрешений в App Web

К счастью для нас, App Web очень похож на любой объект SharePoint SPWeb. Можно управлять разрешениями в App Web так же, как и в Host Web, используя страницу приложений «/_layouts/15/user.aspx». Конечно, эта страница доступна только пользователям с соответствующими разрешениями на Host Web (из-за наследования). Но если вы перейдете на эту страницу из своего веб-приложения, мы увидим, что происходит с разрешениями.

Если у вас есть список, развернутый в App Web, который содержит данные приложения, просто добавьте QueryString на эту страницу «?List=[ListId]», и вы сможете увидеть разрешения этого конкретного списка.

Разорвать цепь

В нашем случае нам нужно, чтобы все пользователи вносили вклад в этот список, но не касались разрешений хост-сайта. Поскольку в нашем приложении был развернут только один список, мы решили разорвать наследование на уровне веб-приложения, и поэтому список наследует все разрешения на уровне веб-приложения. Нам нужно было одноразовое событие от пользователя, которому разрешено делать это в App Web в первую очередь. Здесь нам нужны разрешения приложений на хост-сайте. Идея состоит в том, чтобы позволить администратору установить приложение. При запросе разрешений для приложений администратор предоставляет разрешения, тем самым перенаправляя его в веб-приложение после установки и позволяя нам позволить ему «инициализировать» приложение. На этом этапе инициализации выполняется некоторый код JavaScript, который прерывает наследование ролей и добавляет разрешения Contribute для группы «Все» в App Web.

Установка приложения

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

Теперь, когда администратор доверяет этому приложению, он будет немедленно перенаправлен на страницу по умолчанию в App Web.

Теперь у нас настроено событие, чтобы получить классный скрипт для изменения разрешений App Web.

Прервать наследование ролей

Чтобы разорвать наследование ролей и добавить новое назначение ролей, мы использовали пример Юрия Леонтьева: http://spsite.pro/Blog/Post/3/SharePoint-2013-REST-API-%E2%80%93 -Как-установить-Уникальные-Разрешения-(Разрешения-Уровня-Элементов)

Моя функция для нарушения наследования ролей:

[code lang="js"]
P42.UpdateAppPermissions = function ($) {
var execute = function (appWebUrl) {
var deferred = $.Deferred();
$.ajax({
url: appWebUrl + "/_api/web/breakroleinheritance(copyRoleAssignments = true, clearSubscopes = true)",
type: 'POST',
headers:{ "accept ”: “application/json;odata=verbose”, “content-type”: “application/json;odata=verbose”, “X-RequestDigest”: $(“#__REQUESTDIGEST”).val() },
успех: function () { deferred.resolve(true); },
error: function (sender) { deferred.reject(sender); }
});
return deferred;< br /> };
return {
выполнить: выполнить
}
}(jQuery);
[/code]

Это только нарушит наследование ролей между Host Web и App Web, но также скопирует разрешения по умолчанию.

Добавить назначения ролей

Вторая часть — получить основной идентификатор группы «Все» и добавить назначение ролей, в котором говорится, что у всех есть разрешения на участие. Для этого нам понадобятся две функции.

[code lang="js"]
P42.GetEveryone = function ($) {
var execute = function (appWebUrl) {
var url = "/_api/web/siteusers?$filter =Title eq 'Everyone'$select=Id”;
var deferred = $.Deferred();
$.ajax({
url: appWebUrl + url,
type: 'GET',
заголовки: { «принять»: «application/json;odata=verbose» },
успех: функция (данные) { deferred.resolve(data); },
error: function (sender) { deferred.reject(sender); }
});
return deferred;
};
return {
execute: execute
}
}(jQuery);
[/code]

[code lang="js"]
P42.AddEveryone = function ($) {
var execute = function (appWebUrl, PrincipalId) {
var deferred = $.Deferred();
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var roleDefinition = web.get_roleDefinitions().getById(1073741827);
// Создать новую коллекцию RoleDefinitionBindingCollection
var newBindings = SP.RoleDefinitionBindingCollection.newObject(ctx);
// Добавить роль в коллекцию
newBindings.add(roleDefinition);
// Получить RoleAssignmentCollection для целевого списка
varassigns = web.get_roleAssignments();
// Добавляем пользователя в целевой список и назначаем использование новой коллекции RoleDefinitionBindingCollection
var roleAssignment =assignments.add (web.getUserById(principalId), newBindings);
ctx.executeQueryAsync(function () { deferred.resolve(); }, function (sender, args) { deferred.reject(sender, args); });
возврат отложен;
};
возврат {
выполнить: выполнить
}
}(jQuery);
[/code]

Чтобы получить идентификатор разрешения Contribute по умолчанию, мы можем использовать простой вызов REST в любом SharePoint Web. Этот идентификатор является фиксированным и всегда может использоваться. Из этого обзора мы также можем увидеть другие разрешения, которые мы можем установить, если захотим.

https://your_sp_site.sharepoint.com/_api/web/roledefinitions

Запустить один раз

Хорошо, теперь часть скрипта должна запускаться только один раз. Для того, чтобы это произошло, существует множество различных вариантов, но тот, который я всегда помнил из ранней разработки SharePoint, — это установка свойства в наборе свойств, указывающего, что сценарий уже выполнен. Я создал две функции: одну для проверки того, установлено ли свойство, и одну для его фактической установки.

[code lang="js"]
P42.GetInitProp = function ($) {
var execute = function () {
var deferred = $.Deferred();
var url = _spPageContextInfo.webAbsoluteUrl + «/_api/web/AllProperties/?$select=» + «_AppInit»;
$.ajax({
url: url,
type: 'GET' ,
заголовки: { "accept": "application/json;odata=verbose" },
успех: функция (данные) { deferred.resolve(data); },
ошибка: функция (отправитель) { deferred.reject(sender); }
});
return deferred;
};
return {
выполнить: выполнить
}
}(jQuery);
[/code]

[code lang="js"]
P42.SetInitProp = функция ($) {
var execute = функция (значение) {
var deferred = $.Deferred();
var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var properties = web.get_allProperties();
properties.set_item("_AppInit", значение );
web.update();
ctx.load(web);
ctx.executeQueryAsync(function () { deferred.resolve(); }, function (sender, args) { deferred.reject(sender, args); });
return deferred;
};
return {
execute: execute
}
}(jQuery );
[/код]

На странице по умолчанию мы можем использовать эту часть для проверки свойства.

[code lang="js"]
P42.GetInitProp.execute().promise().then(function (data) {
if (data.d.OData__x005f_AppInit) {
jQuery( '#AfterInit').show();
}else {
jQuery('#PreInit').show();
}
}, функция (отправитель, аргументы) {
//Ошибка
});[/code]

Где мое свойство в этом случае — «_AppInit».

Обещания

Теперь последняя часть этой головоломки — заставить все эти функции работать в контролируемом потоке. Вы, возможно, заметили, что существует некоторая зависимость друг от друга, поэтому jQuery Deferred присутствует во всех этих функциях. Единственная функция, которая вызывает все эти вызовы:

[code lang="js"]
initApp = function (appWebUrl) {
P42.UpdateAppPermissions.execute(appWebUrl).promise().then(function (success) {
P42.GetEveryone .execute(appWebUrl).promise().then(function (data) {
var PrincipalId = data.d.results[0].Id;
P42.AddEveryone.execute(appWebUrl, PrincipalId). promise().then(function () {
//Создание веб-ресурса
P42.SetInitProp.execute(true).promise().then(function () {
//Успех! Делайте другие вещи
},
function (sender, args) {
console.log(args.get_message() + '\n' + args.get_stackTrace());
});
}, function (sender, args) {
console.log(args.get_message() + '\n' + args.get_stackTrace());
});< br /> }, function (sender, args) {
console.log(args.get_message() + '\n' + args.get_stackTrace());
});
} , function (sender, args) {
console.log(args.get_message() + '\n' + args.get_stackTrace());
});
};[/code ]

В основном это все. Нам нужно передать AppWebUrl этому методу. Я думаю, что часть этого кода на самом деле можно сделать лучше, но только для этого примера он должен работать.

Заключительные соображения

С помощью этих фрагментов кода легко установить разрешения приложения так, как вы хотите, за один начальный шаг, однако этот обходной путь не совсем идеален. Несмотря на то, что это сложно и пользователи должны быть настоящими гуру кода, можно злоупотреблять разрешениями приложений, которые мы предоставили. Используя GetScript и междоменную библиотеку, можно проникнуть в Host Web, но, честно говоря, это всегда происходит при использовании разрешений приложений.

Кроме того, использование начального шага, который должен выполнять администратор, также не является идеальным. Тем не менее с данными инструментами на данный момент я думаю, что это лучшее решение для управления разрешениями в App Web. Надеюсь, это улучшится в будущем.

И, наконец, еще одно прекрасное объяснение того, почему прерывание наследования ролей не очень хорошая практика. Очень хорошо объясненное сообщение в блоге из 3 частей.

Сломать не сложно

Разрешения сайта во все тяжкие, эпизод 1

Разрешения сайта во все тяжкие, эпизод 2

Но помните, что с SharePoint это всегда зависит!