Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client security checks RFC #10

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions text/0000-client-security-checks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
- Дата создания: 2019-05-29
- RFC PR:
- RFC Issue:
- Flexberry Issue:

# Инфраструктура для работы с полномочиями на клиенте в ember-flexberry

## Краткое описание

Необходимо реализовать в ember-flexberry инфраструктуру для использования сведений о полномочиях в роутах, контроллерах, шаблонах и сервисах. В том числе реализовать проверку полномочий в базовых роутах создания и редактирования, а также при определении активности кнопок создания, редактирования и удаления на списковых шаблонах и шаблонах редактирования.

## Обоснование

Во-первых, сейчас при недостатке полномочий пользователю просто показывается сообщение с текстом исключения с сервера, что весьма недружественно. Во-вторых, проекты, которые все-таки используют полномочия в каком-то виде, делают это каждый по своему и нет единого подхода. В-третьих, в asp.net web forms была реализована аналогичная логика проверок в базовых классах форм списка и редактирования, что позволяло прикладному разработчику не задумываться об этом аспекте.

## Детальное проектирование

Предлагается внести изменения в два технологических аддона: ember-flexberry и ember-flexberry-security, а также реализовать пакет `NewPlatform.Flexberry.Security.WebApi`, содержащий контроллер, позволяющий запрашивать полномочия через REST API.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю оставаться в пределах OData-функций и экшенов, поскольку, возможна ситуация портирования бакенда на другой стек. Чем более стандартным образом описано API, тем проще может быть выполнен перевод.

Copy link
Contributor Author

@mao29 mao29 Jun 5, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Мы не нашли толковой реализации даже базовой части спецификации OData на java. Шансы на то, что в другом стеке будет реализована спецификация настолько, что даже odata-функции будут поддержаны, мне кажутся крайне маленькими. При этом одата-функции в текущей реализации не позволяют применять правильный DI и затрудняют декомпозицию, потому что являются просто статическими методами. Кроме того, клиентом данного контроллера может быть не только ember-фронтенд, а также, например, Identity Server и лаконичный RESTful API для данной очень ограниченной предметной области гораздо удобнее использовать, чем пытаться из сторонних клиентов формировать корректные odata-запросы.

Средства для реализации и потребления RESTful API есть в любом языке, в то время как средств для реализации odata мы пока не смогли найти.

Так что я за нормальный WebAPI

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю для полномочий использовать все-таки WebAPI по причинам, которые изложил Олег, а также потому, что на прикладных проектах, скорее всего, для подобных задач также уже использовалось WebAPI. Это может облегчить переход.
Думаю также, что работу с полномочиями не обязательно завязывать на те же технологии, что и работу с объектами данных.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Данный функционал очень хорошо ложится в идеалогию микросервиса (на ЕАИС так и сделано).
Сами объекты данных для организации полномочий на клиенте не требуются, достаточны DTO (например, для класса):
{ type: "SomeName", access: ["Read", "Insert"]}
Также часть API должна быть публичной (например, для интеграции с Identity Server), а часть - приватной (получение доступных операций и классов с типами доступа)


#### ember-flexberry:
1. необходимо создать заглушку для сервиса работы с полномочиями `/services/security.js`. Этот сервис должен содержать ряд методов, похожих на методы интерфейса `NewPlatform.Flexberry.Security.ISecurityManager`. Предлагается добавить следующие методы:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Надо подумать какие ещё методы и операции могут пригодиться на клиенте. Это не обязательно должно один в один совпадать с интерфейсом ISecurityManager, поскольку тут всё завязывается на клиентскую, а не на серверную логику.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На клиент лучше всего отдавать все необходимые данные одной пачкой, сохранять в браузер на время жизни всей сессии (logout или истечение токена)

1. `accessCheck(operationName)` - проверка наличия у текущего пользователя полномочий на выполнение операции с заданным именем
2. `accessModelCheck(modelName, typeAccess)` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над объектами заданного типа
3. `accessEntityCheck(model, typeAccess)` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над конкретным переданным объектом
В заглушке предлагается возвращать из всех этих методов RSVP.resolve(true), чтобы по-умолчанию в приложении на ember-flexberry полномочия не контролировались.
При необходимости прикладной разработчик может создать собственную реализацию сервиса `security` или воспользоваться реализацией из аддона ember-flexberry-security.
1. необходимо реализовать хелперы для использования на шаблонах для проверки наличия полномочий на операцию над объектом, а также на именованную операцию
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Помимо использования асинхронных хелперов добавить также режим синхронной проверки полномочий - в этом случае полномочия должны запрашиваться и проверяться до рендеринга формы (в хуке afterModel базового роута). Такое решение позволит избежать "внезапного изменения" внешнего вида формы по мере асинхронной подгрузки полномочий. Для выбора синхронного или асинхронного режима проверки полномочий должна быть предусмотрена соответствующая опция.
Вопрос, который нужно решить в этом случае - делать ли один шаблон для списков с вызовом асинхронных хелперов или в зависимости от значения опции (флага) рендерить разные шаблоны.

1. хелпер `(may typeAccess model)` или `(may typeAccess modelName)`
2. хелпер `(may-do operationName)`
Хелперы должны внутри вызывать соответствующие методы security-сервиса, но нужно учитывать, что эти методы возвращают промисы, поэтому хелпер должен корректно сообщать шаблону о необходимости перевычислить значение после завершения промиса.
3. доработать базовые роуты
1. для роута редактирования, добавить в beforeModel проверку наличия у текущего пользователя полномочий на заданную модель
1. если у пользователя нет полномочий на чтение объектов заданного типа или на чтение конкретного открываемого на редактирование объекта, осуществлять переход на роут с сообщением о недостаточности полномочий и предложением обратиться к администратору
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Внезапно осознал, что лучше бы не на отдельный роут переходить, а просто особый темплейт рендерить, чтобы урл не менялся и пользователи могли скриншоты присылать, по которым понятно было бы на каком роуте проблема
  2. Еще нужно дать какую-то возможность прикладному разработчику тоже отрендерить экран недостаточности полномочий. Может быть и прикладная логика, запрещающая открывать тот или иной роут, хочется чтобы она консистентно выглядела

2. если у пользователя нет полномочий на редактирование объектов заданного типа или на редактирование конкретного открываемого на редактирование объекта, открывать роут в режиме readonly
3. если у пользователя нет полномочий на удаление объектов заданного типа или на удаление конкретного открываемого на редактирование объекта, скрывать кнопку Удалить
2. для роута создания добавить в beforeModel проверку наличия у текущего пользователя полномочий на заданную модель
1. если нет полномочий на чтение объектов заданного типа - см. соответствующий пункт для роута редактирования
2. если нет полномочий на создание объектов заданного типа - осуществлять переход на роут с сообщением о недостаточности полномочий и предложением обратиться к администратору
4. доработать компоненты
1. olv во всех инкарнациях:
1. если у пользователя нет полномочий на чтение объектов заданного типа, показывать текст "У вас недостаточно полномочий на чтение данных объектов. Обратитесь к администратору"
2. если у пользователя нет полномочий на создание объектов заданного типа, не показывать кнопки Создать и Создать на основе
3. если у пользователя нет полномочий на удаление объектов заданного типа, не показывать кнопки удаления в тулбаре и в строках
2. лукап во всех вариантах - если у пользователя нет полномочий на чтение объектов заданного типа - показывать лукап в режиме readonly

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Подумать над событийной моделью обновления полномочий (когда перечитали их с сервера и они изменились).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отписался выше

#### NewPlatform.Flexberry.Security.WebApi
Необходимо создать пакет `NewPlatform.Flexberry.Security.WebApi` с веб-апи контроллером `SecurityController`, который будет принимать в конструктор `ISecurityManager` и реализовывать методы:
1. `AccessCheck(operationName)` - проверка наличия у текущего пользователя полномочий на выполнение операции с заданным именем
2. `AccessModelCheck(modelName, typeAccess)` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над объектами заданного типа
3. `AccessEntityCheck(model, typeAccess)` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над конкретным переданным объектом

#### ember-flexberry-security:
1. необходимо переопределить `/services/security.js`, реализовав получение данных о полномочиях с сервера посредством ajax-запросов к `SecurityController`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Необходимо предусмотреть кеширование полномочий на клиенте. Смотри комментарий к разделу "Нерешенные вопросы".

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Отписался выше

1. необходимо добавить в скрипт установки аддона добавление регистрации сервиса security из аддона в качестве замены для соответствующего сервиса ember-flexberry в прикладном приложении

## Документирование и обучение

Необходимо добавить описание разработанных механизмов в документацию по ember-flexberry, ember-flexberry-security и NewPlatform.Flexberry.Security. Написать инструкции по установке и использованию аддона и нугет-пакета в прикладных проектах.

## Недостатки

1. Некоторые проекты уже реализовали собственную функциональность, аналогичную заявленной. Внедрение данной функциональности на уровне технологии может вступить в конфликт с прикладной логикой, поэтому необходимо привлечь к обсуждению разработчиков прикладных проектов и выработать способы разрешения потенциальных конфликтов после реализации описанной функциональности.
1. Предложенный интерфейс сервиса security и соответствуюшего SecurityController слишком гранулярный. Практика прикладных проектов показывает, что удобнее получить сразу все Permission-ы пользователя с учетом всех его ролей и групп, и далее работать с ними только на клиенте.

## Альтернативы

Вместо разработки собственных хелперов можно попробовать использовать возможности из аддона [[email protected]](https://github.com/minutebase/ember-can/tree/v0.8.5). Более новые версии аддона поддерживают только ember >= 2.12. Этот небольшой аддон содержит инфраструктуру для проверки доступности в приложении определенных *abilities*, в частности, сервис `can` и хелпер `(can 'ability description' model externalProperties)`.

Этот вариант кажется хуже потому, что описанный в документации аддона подход к созданию abilities не предполагает, что для их вычисления возможно использование промисов. Кроме того, в представленной версии аддона нет возможности не передавать в ability какой-то объект в качестве модели, она появилась уже только в более новых версиях. В итоге, чтобы реализовать требуемые возможности, понадобится переписать реализацию хелпера `can` и нарушить концепцию abilities из аддона.

Поскольку исходный код аддона достаточно мал, предпочтительным кажется ознакомиться с ним и почерпнуть и адаптировать основные идеи, однако не добавлять его напрямую как зависимость в ember-flexberry.

## Нерешенные вопросы

Очень хочется кешировать полномочия и результаты проверок на клиенте, но неясно в каком объеме получать данные для кеширования, а также как этот кеш инвалидировать.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Во-первых, необходимо в серверных полномочиях в менеджере полномочий реализовать метод для получения полномочий (сейчас такой метод отсутствует).
Во-вторых, инвалидировать кеш можно через определенный промежуток времени. В настройках приложения необходимо установить интервал проверки "актуальности" полномочий на клиенте (на клиенте должна сохраняться дата/время последней проверки актуальности полномочий). На сервере при этом необходимо предоставить интерфейс для получения информации о дате последнего изменения полномочий. Полномочия с сервера должны отдаваться только в том случае, когда дата/время изменения полномочий на сервере старше, чем на клиенте.
Кроме этого должно быть предусмотрено принудительное обновление кеша полномочий в клиентском приложении с последующим автоматическим обновлением пользовательского интерфейса на текущей (открытой) форме.
Необходимо разработать более конкретные технические описания для кеширования полномочий на клиенте.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

На сервере дата изменений полномочий живёт в полях аудита каждого объекта. Соответственно, достаточно реализовать метод, который сможет возвращать значение максимальной даты полей аудита объектов полномочий.
Кстати, метод принудительного перечитывания полномочий или сброса кеша это ещё один из претендентов на методы сервиса security.js.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Предлагаю добавить к нерешённым вопросам вопрос UI/UX, связанный с асинхронной загрузкой полномочий. При отображении форм данные могут прийти гораздо раньше, чем данные об обновлённых полномочиях. Таким образом пользователю надо каким-то образом сигнализировать, что полномочия подгружаются. Допустим от полномочий зависит доступность для пользователя какой-то кнопки. Пока полномочия не подгрузились каким образом должна выглядеть эта кнопка?

  • Её не должно быть и она появится из пустоты?
  • Она должна быть заблокирована и разблокируется?
  • Она должна быть разблокирована, но по клику выдаст сообщение о не до конца загруженных полномочиях?
    Как должен отображаться прогресс загрузки полномочий? В том числе, когда таких кнопок много и они расположены в разных частях интерфейса.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

А сколько времени будет отрабатывать запрос по полям аудита всех объектов полномочий? Что это вообще должен быть за запрос? union all (select EditTime из каждой таблицы полномочий)?