From ff77d1069a9590e20cd3f3137cfbf1418bcd4aa9 Mon Sep 17 00:00:00 2001 From: "MH.Dmitrii" Date: Sat, 29 Aug 2026 13:42:54 +0300 Subject: [PATCH] readme add --- readme.md | 205 ++++++++++++++++++----------------- src/model/user/user_model.py | 3 - 2 files changed, 104 insertions(+), 104 deletions(-) diff --git a/readme.md b/readme.md index 5f12dbc..7033a50 100644 --- a/readme.md +++ b/readme.md @@ -1,151 +1,154 @@ # Auth & Authorization Service -Backend-приложение на **FastAPI + SQLAlchemy**, реализующее собственную систему -аутентификации и авторизации без использования готовых auth-механизмов фреймворка. +Backend application built with **FastAPI + SQLAlchemy**, implementing a custom +authentication and authorization system without using the framework's built-in +auth mechanisms. -## Стек +## Stack -- **FastAPI** — веб-фреймворк, роутинг, dependency injection -- **SQLAlchemy (ORM)** — доступ к БД, `sessionmaker`, декларативные модели -- **python-jose (jwt)** — создание и валидация JWT-токенов -- **bcrypt / passlib** — хеширование паролей -- **Pydantic v2** — схемы валидации запросов/ответов -- **Alembic** - Миграции бд +- **FastAPI** — web framework, routing, dependency injection +- **SQLAlchemy (ORM)** — database access, `sessionmaker`, declarative models +- **python-jose (jwt)** — creating and validating JWT tokens +- **bcrypt / passlib** — password hashing +- **Pydantic v2** — request/response validation schemas +- **Alembic** — database migrations -## Аутентификация +## Authentication -Аутентификация построена на **JWT-токенах** (Bearer), без серверных сессий. +Authentication is built on **JWT tokens** (Bearer), without server-side sessions. -1. Клиент отправляет `POST /protected/token` с `email` (как `username`) и `password` - в формате `OAuth2PasswordRequestForm` (form-data). -2. Сервер ищет пользователя по email, сверяет пароль через bcrypt +1. The client sends `POST /protected/token` with `email` (as `username`) and + `password` in `OAuth2PasswordRequestForm` format (form-data). +2. The server looks up the user by email and verifies the password via bcrypt (`hash_check(plain, hashed)`). -3. Если пара email/пароль верна — выпускается JWT с payload `{"sub": }`, - подписанный секретным ключом (`ENV.SECRET_STRING`, алгоритм `ENV.ALGORITHM`). -4. Клиент передаёт токен в заголовке `Authorization: Bearer ` в каждом - последующем запросе. -5. На защищённых эндпоинтах зависимость `get_current_user`: - - декодирует токен (`jwt.decode`), - - достаёт `sub` (id пользователя), - - загружает пользователя из БД вместе с его правами, - - при любой ошибке (невалидный/просроченный токен, юзер не найден) — +3. If the email/password pair is correct — a JWT is issued with payload + `{"sub": }`, signed with a secret key (`ENV.SECRET_STRING`, + algorithm `ENV.ALGORITHM`). +4. The client passes the token in the `Authorization: Bearer ` header + with every subsequent request. +5. On protected endpoints, the `get_current_user` dependency: + - decodes the token (`jwt.decode`), + - extracts `sub` (user id), + - loads the user from the DB along with their permissions, + - on any error (invalid/expired token, user not found from the token) — **401 Unauthorized**. -> logout в чистой JWT-схеме требует либо короткого TTL токена + refresh-токена, -> либо чёрного списка отозванных токенов (таблица `revoked_tokens`). -> Также можно просто затирать токен на фронтенде при разлогине, как самый простой вариант. +> Logout in a pure JWT scheme requires either a short token TTL + a refresh +> token, or a blacklist of revoked tokens (a `revoked_tokens` table). +> Alternatively, the token can simply be cleared on the frontend at logout, as +> the simplest option. -## Модель авторизации +## Authorization Model -Вместо классической схемы `roles / business_elements / access_roles_rules` -(с раздельными `read/create/update/delete` × `_all`) выбрана более простая -**flat-модель прав доступа**, которая проще для демонстрации, но покрывает те же -принципы: пользователь получает набор строковых прав (permissions), и каждый -эндпоинт объявляет, какие права требуются для его вызова. +Instead of the classic `roles / business_elements / access_roles_rules` scheme +(with separate `read/create/update/delete` × `_all`), a simpler **flat +permission model** was chosen, which is easier to demonstrate but covers the +same principles: a user is granted a set of string permissions, and each +endpoint declares which permissions are required to call it. -### Таблицы БД +### Database Tables **users** -| поле | тип | описание | -|-------------------|-------------|---------------------------------------------------------------------------| -| id | int, PK | идентификатор пользователя | -| name | str(64) | имя | -| last_name | str(64) | фамилия | -| middle_name | str(64) | отчество | +| field | type | description | +|-------------------|-------------|-----------------------------------------------------------------------------| +| id | int, PK | user identifier | +| name | str(64) | first name | +| last_name | str(64) | last name | +| middle_name | str(64) | middle name | | email | str(255) | email, unique | -| status | bool | активен ли аккаунт (используется вместо `is_active` для мягкого удаления) | -| hashed_password | str(255) | bcrypt-хеш пароля | +| status | bool | whether the account is active (used instead of `is_active` for soft delete) | +| hashed_password | str(255) | bcrypt password hash | **permissions** -| поле | тип | описание | -|-------------|-----------|------------------------------------------------------------------------| -| id | int, PK | идентификатор права | -| permission | str(64) | код права: `admin`, `can_view`, `can_create`, `can_edit`, `can_delete` | +| field | type | description | +|-------------|-----------|--------------------------------------------------------------------------| +| id | int, PK | permission identifier | +| permission | str(64) | permission code: `admin`, `can_view`, `can_create`, `can_edit`, `can_delete` | **user_permission** (association table, many-to-many) -| поле | тип | описание | -|------------------|-----|--------------------------| -| user_id | FK | → users.id | -| permission_id | FK | → permissions.id | +| field | type | description | +|------------------|------|---------------------------| +| user_id | FK | → users.id | +| permission_id | FK | → permissions.id | -Один пользователь может иметь несколько прав одновременно (например, -`can_view` + `can_edit`); право `admin` даёт полный доступ и разрешает -выдавать/забирать любые права, включая `admin`, другим пользователям. +A single user can hold several permissions at once (e.g. `can_view` + +`can_edit`); the `admin` permission grants full access and allows +granting/revoking any permissions, including `admin`, to/from other users. -### Правила проверки доступа +### Access Check Rules -Каждый метод бизнес-логики (`CrudActions`) явно объявляет множество -допустимых прав и проверяет его перед выполнением действия: +Every business-logic method (`CrudActions`) explicitly declares the set of +permitted permissions and checks it before performing the action: -| Действие | Требуемое право | -|----------------------------|---------------------------------| -| Просмотр пользователя (по email/id) | `can_view` или `admin` | -| Создание пользователя | `can_create` или `admin` | -| Изменение пользователя | `can_edit` или `admin` | -| Удаление пользователя | `can_delete` или `admin` | +| Action | Required permission | +|------------------------------|----------------------------------| +| View user (by email/id) | `can_view` or `admin` | +| Create user | `can_create` or `admin` | +| Update user | `can_edit` or `admin` | +| Delete user | `can_delete` or `admin` | -Дополнительно действует правило **защиты от эскалации привилегий**: пользователь -без права `admin` не может назначить право `admin` ни себе, ни другому -пользователю при создании/редактировании аккаунта. +Additionally, a **privilege escalation protection** rule applies: a user +without the `admin` permission cannot grant the `admin` permission to +themselves or any other user when creating/editing an account. -### Обработка ошибок доступа +### Access Error Handling -- **401 Unauthorized** — если запрос не удаётся сопоставить с валидным - залогиненным пользователем (нет токена, токен просрочен/невалиден, юзер - из токена не существует). -- **403 Forbidden** — пользователь определён, но у него нет нужного права - на запрошенное действие/ресурс (в т.ч. попытка эскалации прав, попытка - занять чужой email). -- **404 Not Found** — запрошенный ресурс (пользователь, право) не существует. +- **401 Unauthorized** — if the request cannot be matched to a valid logged-in + user (no token, expired/invalid token, user from the token does not exist). +- **403 Forbidden** — the user is identified, but lacks the required + permission for the requested action/resource (including privilege + escalation attempts, attempts to claim someone else's email). +- **404 Not Found** — the requested resource (user, permission) does not + exist. ## API -| Метод | Путь | Требуемое право | Описание | -|--------|------------------------------------|-------------------------|------------------------------------| -| POST | `/protected/token` | — | Логин, выдача JWT | -| POST | `/protected/logout` | — | Логин, выдача JWT | -| GET | `/protected/get_user_by_email` | `can_view` / `admin` | Получить пользователя по email | -| GET | `/protected/get_user_by_id` | `can_view` / `admin` | Получить пользователя по id | -| POST | `/protected/create_user` | `can_create` / `admin` | Создать пользователя | -| PATCH | `/protected/update_user` | `can_edit` / `admin` | Обновить свой профиль или (для admin/can_edit) чужой через `target_id` | -| DELETE | `/protected/delete_user` | `can_delete` / `admin` | Удалить (деактивировать) пользователя | +| Method | Path | Required permission | Description | +|--------|--------------------------------------|--------------------------|----------------------------------------| +| POST | `/protected/token` | — | Login, JWT issuance | +| POST | `/protected/logout` | — | Login, JWT issuance | +| GET | `/protected/get_user_by_email` | `can_view` / `admin` | Get user by email | +| GET | `/protected/get_user_by_id` | `can_view` / `admin` | Get user by id | +| POST | `/protected/create_user` | `can_create` / `admin` | Create user | +| PATCH | `/protected/update_user` | `can_edit` / `admin` | Update own profile, or (for admin/can_edit) someone else's via `target_id` | +| DELETE | `/protected/delete_user` | `can_delete` / `admin` | Delete (deactivate) user | -## Тестовые данные +## Test Data -Для демонстрации системы в БД должны быть заведены минимум: -Пароли d@d.d, d1@d.d, d2@d.d 12345678 -- 1 пользователь с правом `admin` (полный доступ) -- 1 пользователь с правом `can_view` (только просмотр) -- 1 пользователь без прав (для проверки 403) +For demonstrating the system, the DB must contain at least: +Passwords d@d.d, d1@d.d, d2@d.d 12345678 +- 1 user with the `admin` permission (full access) +- 1 user with the `can_view` permission (view only) +- 1 user with no permissions (for testing 403) -## Структура проекта +## Project Structure ``` src/ - db/ # ActionsDB — доступ к БД (SQLAlchemy sessions, запросы) + db/ # ActionsDB — DB access (SQLAlchemy sessions, queries) model/ - database_model/ # SQLAlchemy-модели (User, Permissions) - user/ # Pydantic-схемы (UserOut, UserCreate, UserUpdate, PermissionIn/Out) - env_read/ # Чтение env через pydantic settings + database_model/ # SQLAlchemy models (User, Permissions) + user/ # Pydantic schemas (UserOut, UserCreate, UserUpdate, PermissionIn/Out) + env_read/ # Reading env via pydantic settings service/ - crud_actions/ # CrudActions — бизнес-логика + проверки прав доступа - JWT/ # JWT (кодирование/декодирование токена), Hash (bcrypt) - auth/ # Создание и проверка jwt токена - errors/ # централизованные HTTP-ошибки (401/403/404) - web/ # роутеры FastAPI (эндпоинты) - migrations/ # миграции бд - tests/ # тесты (Пусто, не создавались) + crud_actions/ # CrudActions — business logic + access permission checks + JWT/ # JWT (token encoding/decoding), Hash (bcrypt) + auth/ # JWT token creation and verification + errors/ # centralized HTTP errors (401/403/404) + web/ # FastAPI routers (endpoints) + migrations/ # DB migrations + tests/ # tests (empty, not yet created) ``` -## Запуск +## Running ```bash poetry make run ``` -Переименовать env \ No newline at end of file +Rename env diff --git a/src/model/user/user_model.py b/src/model/user/user_model.py index 4a3f1bb..ad36f2a 100644 --- a/src/model/user/user_model.py +++ b/src/model/user/user_model.py @@ -1,9 +1,6 @@ from typing import Annotated from pydantic import BaseModel, EmailStr, Field - - - class Base(BaseModel): pass