-
Notifications
You must be signed in to change notification settings - Fork 8
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
base: master
Are you sure you want to change the base?
Changes from all commits
3db6c73
afe7c21
c016869
aa50b50
ac453ee
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
- Дата создания: 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. | ||
|
||
#### NewPlatrofm.Flexberry.Security | ||
1. необходимо добавить в интерфейс `NewPlatform.Flexberry.Security.ISecurityManager` и его реализации методы | ||
1. получения всех полномочий текущего пользователя | ||
2. очистки кеша полномочий | ||
3. получения даты последнего изменения полномочий. Реализация этого метода предполагает получение даты последней записи аудита по объектам полномочий. В будущем его реализация может быть изменена для улучшения производительности или более изощренной логики определения факта изменения полномочий для конкретного пользователя | ||
|
||
#### ember-flexberry: | ||
1. необходимо создать заглушку для сервиса работы с полномочиями `/services/security.js`. Этот сервис должен содержать ряд методов, похожих на методы интерфейса `NewPlatform.Flexberry.Security.ISecurityManager`, а также методы для загрузки и кеширования полномочий текущего пользователя. Предлагается добавить следующие "публичные" методы: | ||
1. `loadCurrentUserPermissions():Promise` - получение с сервера полномочий для текущего пользователя (из сервиса `user`) | ||
1. `reloadCurrentUserPermissions():Promise` - получение с сервера полномочий для текущего пользователя с предварительной очисткой кеша на сервере | ||
1. `accessCheck(operationName):bool` - проверка наличия у текущего пользователя полномочий на выполнение операции с заданным именем | ||
2. `accessModelCheck(modelName, typeAccess):bool` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над объектами заданного типа | ||
3. `accessEntityCheck(model, typeAccess):bool` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над конкретным переданным объектом | ||
В заглушке предлагается возвращать из всех этих методов true, чтобы по-умолчанию в приложении на ember-flexberry полномочия не контролировались. | ||
При необходимости прикладной разработчик может создать собственную реализацию сервиса `security` или воспользоваться реализацией из аддона ember-flexberry-security. | ||
1. необходимо реализовать хелперы для использования на шаблонах для проверки наличия полномочий на операцию над объектом, а также на именованную операцию | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. если у пользователя нет полномочий на чтение объектов заданного типа или на чтение конкретного открываемого на редактирование объекта, отображать плашку с сообщением о недостаточности полномочий и предложением обратиться к администратору. Хук model должен возвращать `Promise.reject()`, чтобы прикладная логика в `then`, `model` и `afterModel` могла корректно обработать данную ситуацию. | ||
2. если у пользователя нет полномочий на редактирование объектов заданного типа или на редактирование конкретного открываемого на редактирование объекта, открывать роут в режиме readonly | ||
3. если у пользователя нет полномочий на удаление объектов заданного типа или на удаление конкретного открываемого на редактирование объекта, скрывать кнопку Удалить | ||
2. для роута создания добавить в beforeModel проверку наличия у текущего пользователя полномочий на заданную модель | ||
1. если нет полномочий на чтение или создание объектов заданного типа - см. соответствующий пункт для роута редактирования | ||
4. доработать компоненты | ||
1. olv во всех инкарнациях: | ||
1. если у пользователя нет полномочий на чтение объектов заданного типа, показывать текст "У вас недостаточно полномочий на чтение данных объектов. Обратитесь к администратору" | ||
2. если у пользователя нет полномочий на создание объектов заданного типа, не показывать кнопки Создать и Создать на основе | ||
3. если у пользователя нет полномочий на удаление объектов заданного типа, не показывать кнопки удаления в тулбаре и в строках | ||
2. лукап во всех вариантах - если у пользователя нет полномочий на чтение объектов заданного типа - показывать лукап в режиме readonly | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Подумать над событийной моделью обновления полномочий (когда перечитали их с сервера и они изменились). There was a problem hiding this comment. Choose a reason for hiding this commentThe 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)` - проверка наличия у текущего пользователя полномочий на выполнение заданного типа операций над конкретным переданным объектом | ||
4. `GetCurrentUserPermissions()` - получение все полномочия текущего пользователя (из сервиса CurrentUserService+IUser) | ||
5. `GetPermissionsLastChangeDate():DateTime` - получение даты последнего изменения полномочий | ||
6. `ClearCache()` - сброс кеша полномочий | ||
|
||
#### ember-flexberry-security: | ||
1. необходимо переопределить `/services/security.js` | ||
1. метод `loadCurrentUserPermissions` должен посылать ajax-запрос к `SecurityController` и возвращает соответствующий промис. Результат промиса запоминается в локальном кеше, также как время, на которое получены эти данные | ||
2. метод `reloadCurrentUserPermissions` должен посылать запрос на очистку кеша и затем выполнять то же действие, что `loadCurrentUserPermissions` | ||
3. пока полномочия не вычитаны с сервера, возвращать из всех методов проверки полномочий пользователя `true`, чтобы пользователь, если у него недостаточно прав, получал сообщения с ошибками от сервера | ||
4. методы проверки полномочий пользователя должны возвращать данные на основе кеша. При этом должно проверяться время последнего получения полномочий и сравниваться с интервалом кеширования из конфига. Если интервал кеширования прошел, должен запускаться фоновый запрос `GetPermissionsLastChangeDate`. Если полученное значение меньше, чем время последней загрузки полномочий, то в поле времени последней загрузки полномочий должно вписываться текущее время. Если полученное значение больше, чем время последней загрузки полномочий, в фоне должен запускаться метод `reloadCurrentUserPermissions` | ||
1. необходимо добавить в скрипт установки аддона | ||
1. добавление регистрации сервиса security из аддона в качестве замены для соответствующего сервиса ember-flexberry в прикладном приложении | ||
2. добавление в environment.js секции с настройкой интервала кеширования полномочий | ||
|
||
## Документирование и обучение | ||
|
||
Необходимо добавить описание разработанных механизмов в документацию по ember-flexberry, ember-flexberry-security и NewPlatform.Flexberry.Security. Написать инструкции по установке и использованию аддона и нугет-пакета в прикладных проектах. | ||
|
||
## Недостатки | ||
|
||
1. Некоторые проекты уже реализовали собственную функциональность, аналогичную заявленной. Внедрение данной функциональности на уровне технологии может вступить в конфликт с прикладной логикой, поэтому необходимо привлечь к обсуждению разработчиков прикладных проектов и выработать способы разрешения потенциальных конфликтов после реализации описанной функциональности | ||
2. Запрос на получение даты последнего изменения полномочий потенциально очень неэффективен, а при предлагаемой реализации может вызываться часто и большим количеством пользователей | ||
|
||
## Альтернативы | ||
|
||
Вместо разработки собственных хелперов можно попробовать использовать возможности из аддона [[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. | ||
|
||
## Нерешенные вопросы | ||
|
||
1. Нужно придумать формат возвращаемых с сервера полномочий пользователя (с учетом всех его ролей и наследования). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Предлагаю оставаться в пределах OData-функций и экшенов, поскольку, возможна ситуация портирования бакенда на другой стек. Чем более стандартным образом описано API, тем проще может быть выполнен перевод.
There was a problem hiding this comment.
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
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Предлагаю для полномочий использовать все-таки WebAPI по причинам, которые изложил Олег, а также потому, что на прикладных проектах, скорее всего, для подобных задач также уже использовалось WebAPI. Это может облегчить переход.
Думаю также, что работу с полномочиями не обязательно завязывать на те же технологии, что и работу с объектами данных.
There was a problem hiding this comment.
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), а часть - приватной (получение доступных операций и классов с типами доступа)