Skip to content
This repository has been archived by the owner on Sep 25, 2024. It is now read-only.

Commit

Permalink
feat: Реализована документация для паттерна реализации доступов
Browse files Browse the repository at this point in the history
feat: Реализована документация для паттерна реализации доступов
  • Loading branch information
AndTem authored May 22, 2024
2 parents 3fc8953 + 8bfe518 commit 6fd0164
Show file tree
Hide file tree
Showing 14 changed files with 1,578 additions and 0 deletions.
16 changes: 16 additions & 0 deletions .cspell-ignore
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ webp
филде
филдов
фича
фичи
флоу
хендлер
ховере
Expand All @@ -343,3 +344,18 @@ webp
экшенов
эмитит
эмитом
рутов
переиспользование
переиспользованы
переиспользуемые
переиспользуются
переиспользования
ABAC
бэкенде
имплементированный
модалке
нейминг
jsdoc
дебагинга
админские
Permissioning
99 changes: 99 additions & 0 deletions docs/permissions/2fa.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
sidebar_position: 10
---

# 2FA (Two-factor Auth)

Permissions могут работать сообща с `2FAService`.

## Пример

**Требования**

Создание документа доступно только администратору с активной 2FA.

**Реализация**

```modules/permissions/domain/stores/PermissionsStore/policies/AdministrationPolicyStore```
```ts
export class AdministrationPolicyStore {
constructor(
private readonly policyManager: PolicyManagerStore,
private readonly userRepo: UserRepository,
private readonly twoFA: TwoFAService,
) {
makeAutoObservable(this, {}, { autoBind: true });

this.policyManager.registerPolicy({
name: 'administration',
prepareData: async (): Promise<void> => {
await Promise.all([this.userRepo.getRolesQuery().async()]);
},
});
}

/**
* Доступ к действиям администратора
*/
public get administrationActions() {
return this.policyManager.processPermission((allow, deny) => {
// Если twoFA не пройдена, то отказываем в доступе с причиной, которая будет обработана в features
// Также, по необходимости, можно вызвать логику запроса от пользователя прохождения 2FA
if (!twoFA.isPassed) {
return deny(PermissionDenialReason.TwoFA);
}

if (this.userRepo.getRolesQuery().data?.isAdmin) {
return allow();
}

deny(PermissionDenialReason.NoAdmin);
});
}
}
```

```modules/layout/features/MainLayout/Sidebar/UIStore```
```ts
export class UIStore {
constructor(
private readonly permissions: PermissionsStore,
private readonly twoFA: TwoFAService,
private readonly notifyService: Notify,
private readonly router: Router,
) {
makeAutoObservable(this, {}, { autoBind: true });
}

public openDocCreation = () => {
const permission = this.permissions.administration.administrationActions;

if (permission.isAllowed) {
this.router.push(APP_ROUTES.createDoc.getRedirectPath());

return;
}

if (permission.hasReason('no-admin')) {
this.notifyService.error(
'Доступно только администратору'
);

return;
}

if (permission.hasReason('2fa')) {
// Здесь можно, например, реализовать открытие модалки для 2FA
this.notifyService.error(
'Нужно пройти 2FA',
);

return;
}

this.notifyService.error(
'Добавить документ на полку нельзя. Попробуйте сменить аккаунт',
);
};
}
```
8 changes: 8 additions & 0 deletions docs/permissions/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"label": "Разграничение доступов (permissions)",
"position": 1,
"link": {
"type": "generated-index",
"description": "Паттерн реализации системы доступов на клиенте"
}
}
66 changes: 66 additions & 0 deletions docs/permissions/core.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
sidebar_position: 1
---

# Вдохновлено ABAC и XAML

Описанный паттерн берет за основу принципы ABAC (Attribute-Based Access Control) и [XAML](https://habr.com/ru/companies/custis/articles/258861/).

## На клиенте нужен ABAC, даже если на бэкенде RBAC

В [этой статье](https://habr.com/ru/companies/custis/articles/248649/) вы можете прочитать чем концептуально отличаются ABAC (Attribute-Based Access Control) и RBAC (Role Based Access Control).

На Frontend'е нужна такая система доступов, которая однозначно будет указывать на предмет блокировок доступов в контексте пользовательского интерфейса.
RBAC и даже ABAC, имплементированный на стороне API не будет зависеть от UI, который привносит свою дополнительную специфичность при расчете доступов.

В связи с этим, на клиенте необходимо перманентно использовать ABAC, доступы которого будут напрямую связаны со специфичностью UI.

### Пример с RBAC на бэкенде

**Предметная область**

ЭДО

**Роли**

- PaymentAccount
- FreeAccount

**Бизнес требование**

Кнопка "Создать документ" доступна для пользователя с платным аккаунтом и если у него есть созданная организация.

**Проблема**

Из требования видно, что для вычисления доступа не хватает только одного знания о роле пользователя (роль `PaymentAccount`), необходимо дополнительно учитывать есть у пользователя организация.

**Неверное решение**

Добавить к кнопке дополнительный `if` на проверку организации.
Это приведет к "размазыванию" логики доступов по приложению и последующую потерю контроля.

**Правильное решение**

Использование ABAC с permission `docActions`, который внутри будет проверять роль пользователя и наличие организации.
`docActions` будет использоваться в необходимых частях приложения для проверки доступа к действиям на документом.

### Пример с ABAC на бэкенде

**Предметная область**

ЭДО

**Permissions с API**
- docActions

**Бизнес требование**

Кнопка "Создать документ" доступна для пользователя с платным аккаунтом и если он прошел двух факторную аутентификацию.

**Проблема**

В `docActions` на бэкенде проверяется оплачен ли аккаунт пользователя, но проверку на 2FA можно провести только в runtime при нажатии на саму кнопку.

**Решение**

Добавить в едином клиентском сервисе доступов дополнительную логику для `docActions`, которая будет проверять 2FA.
7 changes: 7 additions & 0 deletions docs/permissions/example.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
sidebar_position: 11
---

# Пример реализации доступов

In Progress
20 changes: 20 additions & 0 deletions docs/permissions/featureToggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
sidebar_position: 9
---

# Feature Toggle и Permissions

Feature Toggle и Permissions - это две разные концепции.

**Feature Toggle** необходим в большинстве случаев для временного выключения функционала. Фича флаги - это временные условия, которые в будущем должны быть удалены.
Например, после тестирования на фокусной группе или конечной реализации функционала.

В свою очередь **Permissions** - это постоянные условия системы, которые изменяются редко. Изменения происходит только при изменении бизнес требований.

**Нельзя смешивать концепции Feature Toggle и Permissions в одной абстракции**, в приложении должно быть два отдельных сервиса.

## Permissions может использовать Feature Toggle

Permissions может использовать Feature Toggle только в том случае, если Feature Toggle предоставляет [Permissioning Toggles](https://martinfowler.com/articles/feature-toggles.html#CategoriesOfToggles:~:text=Operations%20person%20happy.-,Permissioning%20Toggles,-turning%20on%20new).

В противном случае Feature Toggle должен использоваться отдельно от Permissions.
31 changes: 31 additions & 0 deletions docs/permissions/motivation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
sidebar_position: 0
---

# Мотивация

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

## Необходимо применять когда

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

## Применение паттерна позволяет

- Уменьшить затраты на разработку системы доступов
- Иметь единую реализацию системы доступов во всех приложениях компании
- Централизованно развивать паттерн
- Избежать повторения одних и тех же ошибок реализации в разных приложениях

## Паттерн предоставляет

- Удобный и поддерживаемый способ формирования доступов на клиенте
- Систему причин отказа в доступе. Позволяет улучшить UX и DX при работе с доступами: пользователь и разработчик получают конкретную причину отказа в доступе
- Подход загрузки данных для формирования доступов
- Закрытие рутов приложения (RouteGuards)
- Взаимодействие с другими сервисами. Например, FeatureToggle
Loading

0 comments on commit 6fd0164

Please sign in to comment.