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

Conversation

mao29
Copy link
Contributor

@mao29 mao29 commented May 29, 2019

Написал RFC на клиентские проверки полномочий

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

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.

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


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

Очень хочется кешировать полномочия и результаты проверок на клиенте, но неясно в каком объеме получать данные для кеширования, а также как этот кеш инвалидировать.
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 из каждой таблицы полномочий)?


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

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

#### 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 или истечение токена)


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

Очень хочется кешировать полномочия и результаты проверок на клиенте, но неясно в каком объеме получать данные для кеширования, а также как этот кеш инвалидировать.
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.

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.

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


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

Очень хочется кешировать полномочия и результаты проверок на клиенте, но неясно в каком объеме получать данные для кеширования, а также как этот кеш инвалидировать.
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, связанный с асинхронной загрузкой полномочий. При отображении форм данные могут прийти гораздо раньше, чем данные об обновлённых полномочиях. Таким образом пользователю надо каким-то образом сигнализировать, что полномочия подгружаются. Допустим от полномочий зависит доступность для пользователя какой-то кнопки. Пока полномочия не подгрузились каким образом должна выглядеть эта кнопка?

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

@ehaberev
Copy link
Member

ehaberev commented Jun 19, 2019

@mao29 @bratchikov
Предлагаю внести следующие поправки в RFC и запустить Final Comment Period:

  1. С асинхронными полномочиями не заморачиваться, и подгружать полномочия при входе пользователя в систему. Загрузку полномочий выполнять в роуте application.js, чтобы иметь возможность взаимодействовать с DOM при необходимости. Если потребуется полномочия подгружать асинхронно, сделать отдельный RFC, в котором обговорить, как это должно выглядеть с точки зрения пользовательского интерфейса, привлекая при этом к обсуждению прикладные проекты и "UI-команду".
  2. В раздел про детальное проектирование добавить необходимость внести следующие изменения в интерфейс ISecurityManager и его реализации (для серверных полномочий):
    • Добавить метод для получения всех полномочий пользователя
    • Добавить метод для очистки кеша полномочий
  3. В контроллере SecurityController пакета NewPlatform.Flexberry.Security.WebApi реализовывать методы (перечисленные там на данный момент методы для синхронного варианта полномочий будут, скорее всего, не нужны):
    • Получения всех полномочий пользователя
    • Получение даты последнего изменения полномочий
    • Очистки кеша полномочий
  4. В эмберном сервисе security.js добавить методы, реализующие запросы из п.3 к SecurityController.
  5. Кеширование на клиенте предлагаю реализовать следующим образом:
    • Первоначальные загрузка и кеширование полномочий осуществляется при старте приложения в роуте application.js.
    • В сервисе security.js должны сохраняться дата/время последней загрузки полномочий с сервера и последней проверки актуальности полномочий.
    • В настройках приложения необходимо установить интервал проверки "актуальности" полномочий на клиенте.
    • В базовых роутах списковых форм и форм редактирования в хуке beforeModel или afterModel смотреть дату/время последней проверки актуальности полномочий (хранится в сервисе security.js). Если с этого момента уже прошло время, указанное в настройках приложения (интервал проверки "актуальности" полномочий на клиенте), выполнить запрос на получение даты последнего изменения полномочий на сервере. Если дата/время изменения полномочий на сервере старше, чем на клиенте, должен выполняться запрос на получение полномочий пользователя (сброс кеша на клиенте и "перевычитывание" полномочий с сервера). Все перечисленные запросы должны выполняться внутри промиса в хуке beforeModel или afterModel, этот основной промис резолвится после только после выполнения всех необходимых запросов и их последующей обработки. На время выполнения запросов в приложении должна отображаться "крутилка", сигнализирующая об ожидании перехода на новый роут.
    • Все проверки полномочий в шаблонах соответственно будут исключительно синхронные, можно также их делать с помощью хелперов, но при этом хелперы уже используют закешированные на клиенте в сервисе security.js полномочия.
    • Если для конкретной формы редактирования полномочия на сервере обновились в процессе редактирования данных на этой форме, то предлагаю в такой ситуации полагаться на проверку полномочий на сервере перед сохранением объекта данных (пользователь, возможно, получит ошибку с сервера при попытке сохранить данные).
  6. Нерешенных вопросов в RFC перед его принятием остаться не должно.

@ehaberev ehaberev self-assigned this Jun 19, 2019
@mao29
Copy link
Contributor Author

mao29 commented Jun 20, 2019

  1. По поводу метода очистки кеша полномочий - мне кажется его не нужно добавлять в ISecurityManager. Кеширование это деталь конкретной реализации DefaultSecurityManager. Эта реализация как раз принимает ICacheService как зависимость. Если и делать метод очистки кеша, то у ICacheService. В каком месте, кроме SecurityController, и в какой момент предполагалось сбрасывать кеш? Если только в контроллере, то нужно в него в конструктор заинжектить помимо SecurityManager-а еще и ICacheService его рядом и сбрасывать кеш у этого сервиса по запросу. Но все равно пока не понятно как понимать когда сбрасывать кеш.

  2. По поводу проверок актуальности кеша в сервисе security.js. А почему не внутри самого сервиса логику устаревания реализовать? Мне кажется наиболее удобен вариант, когда сервис всегда возвращает то, что у него в данный момент в кеше, но, если понимает при очередном вызове метода проверки полномочий, что пора кеш обновить, то в фоне запускает промис, который, зарезолвившись, продлит срок годности или обновит содержимое кеша уже постфактум. Да, на форме у нас будут заведомо неправильные полномочия, но при следующем заходе на нее они уже обновятся, а если пользователь попробует что-то сохранить, получит ошибку с сервера - мы такой сценарий все равно допускаем в каких-то случаях. Зато пользователь не будет лишний раз ждать перевычитки полномочий.

  3. Метод получения даты последнего изменения полномочий только в SecurityController-е добавиться должен? В ISecurityService ведь тоже нужен?

@ehaberev
Copy link
Member

  1. По поводу метода очистки кеша полномочий - мне кажется его не нужно добавлять в ISecurityManager. Кеширование это деталь конкретной реализации DefaultSecurityManager. Эта реализация как раз принимает ICacheService как зависимость. Если и делать метод очистки кеша, то у ICacheService. В каком месте, кроме SecurityController, и в какой момент предполагалось сбрасывать кеш? Если только в контроллере, то нужно в него в конструктор заинжектить помимо SecurityManager-а еще и ICacheService его рядом и сбрасывать кеш у этого сервиса по запросу. Но все равно пока не понятно как понимать когда сбрасывать кеш.

У ICacheService есть метод ClearCache. Соответственно там это уже предусмотрено.
В этом контексте мы говорим про кеш у серверных полномочий, поэтому когда его сбрасывать - это вопрос конкретных прикладных потребностей. На уровне технологии кеш полномочий на сервере сбрасывается при любых изменениях в объектах данных серверных полномочий.
Поскольку иногда на прикладных проектах все-таки возникает необходимость принудительно сбросить кеш для серверных полномочий, я предложил соответствующий метод вынести на уровень SecurityController. Но эта возможность в общем-то никак не связана с кешированием полномочий на клиенте, про которую мы говорим в этом RFC.

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

На уровне почти всех реализаций ISecurityManager, насколько я помню, уже есть метод CleearCache для сброса кеша. По крайней мере в SecurityManager он точно есть. Разные реализации ISecurityManger по-разному реализуют кеширование внутри себя (но не все реализуют все-таки, вроде). Поэтому если мы будем сбрасывать кеш через ICacheService, то это будет актуально только для "новых" полномочий (только для реализации с именем SecurityManager). Если этого достаточно, тогда можно делать так.

  1. По поводу проверок актуальности кеша в сервисе security.js. А почему не внутри самого сервиса логику устаревания реализовать? Мне кажется наиболее удобен вариант, когда сервис всегда возвращает то, что у него в данный момент в кеше, но, если понимает при очередном вызове метода проверки полномочий, что пора кеш обновить, то в фоне запускает промис, который, зарезолвившись, продлит срок годности или обновит содержимое кеша уже постфактум. Да, на форме у нас будут заведомо неправильные полномочия, но при следующем заходе на нее они уже обновятся, а если пользователь попробует что-то сохранить, получит ошибку с сервера - мы такой сценарий все равно допускаем в каких-то случаях. Зато пользователь не будет лишний раз ждать перевычитки полномочий.

Согласен, можно реализовать так.

  1. Метод получения даты последнего изменения полномочий только в SecurityController-е добавиться должен? В ISecurityService ведь тоже нужен?

Если я правильно понял, речь идет про ISecurityManager? Если получение даты последнего изменения полномочий актуальна пока только для "новых" полномочий, можно на уровень интерфейса не выносить, а добавить метод только внутри реализации SecurityManager. Но тогда в коде SecurityController надо будет использовать явное приведение типа к этой реализации для менеджера полномочий. Если это не красиво или будет полезно также для "старых" полномочий, можно вынести также на уровень интерфейса ISecurityManager.

Хелперы должны внутри вызывать соответствующие методы 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. Еще нужно дать какую-то возможность прикладному разработчику тоже отрендерить экран недостаточности полномочий. Может быть и прикладная логика, запрещающая открывать тот или иной роут, хочется чтобы она консистентно выглядела

@mao29 mao29 requested a review from bratchikov March 16, 2021 12:05
mao29 and others added 3 commits March 19, 2021 17:30
- Change SecurityController API
- Change security.js API and logic
- Change routes logic according to permission checks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants