Implement activation checks across various commands and queries
- Introduced `IRequiresActivation` interface to enforce activation requirements for multiple commands and queries, ensuring that only activated users can create, edit, or access configurations, news, and applications. - Updated the `RequireActivationBehavior` to handle activation checks uniformly, returning appropriate errors for unauthenticated or inactive users. - Enhanced error handling by adding `NotActivated` error to provide clear feedback for users attempting to access restricted features. - Updated documentation to reflect the new activation requirements and their implications on user access and functionality.
This commit is contained in:
+3
-3
@@ -79,8 +79,8 @@ status, createdAt }`. `expiresAt` всегда `null` (лимиты по сро
|
||||
запрашивает `GET .../link` отдельно, по кнопке на карточке конфига; QR строится на фронте из
|
||||
`connectionString`.
|
||||
|
||||
`POST /api/configs` без активации → `403` (`Configs.NotActivated`); сверх квоты роли → `409`
|
||||
(`Configs.QuotaExceeded`).
|
||||
Все `/api/configs/*`, `/api/news`, `/api/apps` без активации → `403` (`Auth.NotActivated`, единая
|
||||
проверка `RequireActivationBehavior`); создание сверх квоты роли → `409` (`Configs.QuotaExceeded`).
|
||||
|
||||
## Apps — каталог приложений
|
||||
|
||||
@@ -222,7 +222,7 @@ totalConfigs, activeConfigs, totalUsedUpBytes, totalUsedDownBytes }` — счи
|
||||
| --- | -------------------------------------------------------------------- |
|
||||
| 400 | Ошибка валидации (FluentValidation, не на все команды — см. [backend-conventions.md](backend-conventions.md)) |
|
||||
| 401 | Нет/просрочен/невалиден access-токен |
|
||||
| 403 | Нет прав по роли, либо `Configs.NotActivated` |
|
||||
| 403 | Нет прав по роли, либо `Auth.NotActivated` |
|
||||
| 404 | Ресурс не найден |
|
||||
| 409 | Конфликт домена: `Configs.QuotaExceeded`, дубликат имени пользователя при регистрации, уже есть `Pending`-запрос активации |
|
||||
| 422 | Прочие управляемые ошибки, не подошедшие под коды выше |
|
||||
|
||||
+11
-7
@@ -73,9 +73,11 @@ PnvPanel — backend на **ASP.NET Core (.NET 10)** по принципам **C
|
||||
[backend-conventions.md](backend-conventions.md)).
|
||||
- **DTO**: плоские `record`, конвертация из сущностей — статический метод `FromDomain(...)` на самом
|
||||
DTO, без маппера (Mapster/AutoMapper).
|
||||
- **Pipeline behaviors**: `ValidationBehavior`, `LoggingBehavior`, `UnitOfWorkBehavior` (транзакция +
|
||||
`SaveChangesAsync` на команду). Отдельного `AuthorizationBehavior` нет — авторизация (роль,
|
||||
активация) — это либо `RequireAuthorization()`/`RequireRole(...)` на эндпоинте, либо явная проверка
|
||||
- **Pipeline behaviors** (порядок: Logging → Validation → RequireActivation → UnitOfWork):
|
||||
`LoggingBehavior`, `ValidationBehavior`, `RequireActivationBehavior` (403 `Auth.NotActivated` для
|
||||
запросов с маркером `IRequiresActivation` — конфиги, новости, каталог приложений), `UnitOfWorkBehavior`
|
||||
(транзакция + `SaveChangesAsync` на команду). Отдельного `AuthorizationBehavior` для ролей нет —
|
||||
роль проверяется через `RequireAuthorization()`/`RequireRole(...)` на эндпоинте либо явной проверкой
|
||||
в начале хендлера (например, «инбаунд доступен роли пользователя»).
|
||||
- **Result<T>**: явная модель успеха/ошибки (`Result`/`Result<T>`, `Error` с `ErrorType`) вместо
|
||||
исключений для управляемых сценариев.
|
||||
@@ -131,10 +133,11 @@ PnvPanel — backend на **ASP.NET Core (.NET 10)** по принципам **C
|
||||
Пример потока «создать конфиг» (`backend/src/PnvPanel.Application/Configs/Create/CreateVpnConfigCommandHandler.cs`):
|
||||
```
|
||||
POST /api/configs
|
||||
→ CreateVpnConfigCommand
|
||||
→ CreateVpnConfigCommand (IRequiresActivation)
|
||||
→ ValidationBehavior (FluentValidation — формат inboundId/label)
|
||||
→ RequireActivationBehavior (403 Auth.NotActivated, если аккаунт не активирован)
|
||||
→ CreateVpnConfigCommandHandler
|
||||
· проверяет активацию + роль инбаунда (доменные проверки)
|
||||
· проверяет роль инбаунда (доменная проверка)
|
||||
· SELECT pg_advisory_xact_lock(hashtext(userId)) — сериализует параллельные создания
|
||||
· пересчитывает текущее число активных конфигов и сверяет с AppRole.MaxConfigs
|
||||
· IXuiPanelGateway.AddClientAsync(node, inbound, ...) // 3x-ui, получает ClientExternalId
|
||||
@@ -253,8 +256,9 @@ POST /api/configs
|
||||
вошедшим — `POST /api/auth/change-password`.
|
||||
- **AuthZ**: именованных policy нет — либо `.RequireAuthorization()` (любой вошедший) или
|
||||
`.RequireAuthorization(policy => policy.RequireRole(RoleNames.Admin))` на группе эндпоинтов, либо
|
||||
явная проверка внутри хендлера (владение конфигом — сравнение `VpnConfig.UserId` с `ICurrentUser`;
|
||||
активация — `ConfigErrors.NotActivated`).
|
||||
явная проверка внутри хендлера (владение конфигом — сравнение `VpnConfig.UserId` с `ICurrentUser`).
|
||||
Активация — не в хендлере, а в pipeline behavior (`RequireActivationBehavior`, срабатывает на
|
||||
запросах с маркером `IRequiresActivation`: конфиги, новости, каталог приложений) — `AuthErrors.NotActivated`.
|
||||
- **Секреты нод**: шифруются `ISecretProtector` (Data Protection) перед сохранением; в API/логи не попадают.
|
||||
- **CSRF**: явного анти-CSRF токена нет — все мутации API читают авторизацию только из
|
||||
`Authorization: Bearer` (JS должен явно прочитать access-token из памяти и подставить заголовок,
|
||||
|
||||
@@ -107,7 +107,10 @@ NewsPost (лента новостей; публикует
|
||||
трогает уже созданных клиентов в 3x-ui (см. `IXuiPanelGateway.UpdateClientAsync`, где `LimitIp`
|
||||
всегда `null` — «не менять»).
|
||||
- `UpdateTraffic(up, down)` → пишет `TrafficSyncService` при периодической синхронизации, только для отображения.
|
||||
- **Создание разрешено только активированному пользователю** (`AppUser.IsActivated == true`).
|
||||
- **Доступ разрешён только активированному пользователю** (`AppUser.IsActivated == true`): создание,
|
||||
просмотр списка, редактирование, ротация, отзыв, получение ссылки/подписки на свои конфиги, а также
|
||||
чтение новостей и каталога приложений — единая проверка в `RequireActivationBehavior` (pipeline
|
||||
behavior, маркер `IRequiresActivation` на команде/запросе), а не разбросанные проверки в хендлерах.
|
||||
- Число активных конфигов пользователя не может превышать **квоту его роли** (`AppRole.MaxConfigs`;
|
||||
роль `admin` — без лимита). У пользователя ровно одна роль. См. `AppRole` ниже.
|
||||
- Инбаунд должен быть доступен роли пользователя (`Inbound.AllowedRoles`).
|
||||
@@ -196,7 +199,8 @@ NewsPost (лента новостей; публикует
|
||||
| `TelegramLinkedAt` | `DateTimeOffset?` | Когда привязан |
|
||||
|
||||
Инварианты: один `TelegramUserId` ↔ один аккаунт (повторная привязка требует `/unlink`);
|
||||
неактивированный пользователь не может создавать конфиги; при регистрации выдаётся роль `user`.
|
||||
неактивированный пользователь не имеет доступа к конфигам, новостям и каталогу приложений (см. выше);
|
||||
при регистрации выдаётся роль `user`.
|
||||
**Блокировка** (`IsBlocked = true`) переводит все конфиги в `Disabled` (отключение клиентов в 3x-ui);
|
||||
разблокировка включает их обратно. У пользователя ровно одна роль.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user