diff --git a/README.md b/README.md new file mode 100644 index 0000000..9880f09 --- /dev/null +++ b/README.md @@ -0,0 +1,269 @@ +# Лабараторная 2 +Минимальный костяк сервиса управления пользователями и OAuth-клиентами. Предоставляет REST API по пути `/auth/*`, GraphQL-эндпоинт по адресу `/graphql` и небольшое админ-SPA на чистом JS, отдаваемое по `/admin`. + +Основные технологии: +- Python 3.12 +- **FastAPI** - REST API (`/auth/register`, `/auth/login`, `/auth/me`) +- **Ariadne** - GraphQL-сервер +- **SQLAlchemy 2.x** - ORM +- **Alembic** - миграции +- **SQLite** - база данных +- **argon2-cffi** - хеширование паролей Argon2id + стойкость через `zxcvbn` +- **python-jose** - JWT токены + +## Установка + +```sh +git clone https://vantablacktea.ddns.net/Agzamus/scripts-2.git +cd scripts-2 +``` + +### С помощью uv + +[Скачать uv](https://docs.astral.sh/uv/) + +```sh +uv sync +uv run python run.py +``` + +Тесты: +```sh +uv run pytest -q +``` + +### Альтернатива с pip + +Linux: +```sh +python -m venv .venv +source .venv/bin/activate +pip install -r requirements.txt +``` + +Windows: +```sh +python -m venv .venv +source .venv\Scripts\activate +pip install -r requirements.txt +``` + +## Настройка + +Необходимо создать `.env` файл, например скопировав пример: + +```sh +cp .env.example .env +``` + +> Записать для `JWT_SECRET` и `PASSWORD_PEPPER` длинные случайные строки +> `JWT_SECRET` подписывает каждый access-токен, а `PASSWORD_PEPPER` добавляется к паролю перед хешированием + +Переменные из `.env.example`: + +| Переменная | Пример | Назначение | +| ---------------------- | ----------------------------------------- | ------------------------------------------------- | +| `DATABASE_URL` | `sqlite:///./vault.db` | Строка подключения SQLAlchemy | +| `JWT_SECRET` | `replace_with_a_long_random_string` | Секрет для подписи/проверки JWT | +| `JWT_ALGORITHM` | `HS256` | Алгоритм подписи JWT | +| `ACCESS_TOKEN_TTL_MIN` | `60` | Время жизни access-токена в минутах | +| `PASSWORD_PEPPER` | `replace_with_another_long_random_string` | Глобальный секрет, добавляемый перед хешированием | +| `PASSWORD_MIN_LENGTH` | `12` | Минимальная длина пароля | +| `ENVIRONMENT` | `development` | Метка окружения выполнения | +| `APP_NAME` | `Vault` | Заголовок приложения | + +## Запуск + +Примените миграции базы данных, затем запустите сервер: + +```bash +uv run alembic upgrade head # создать/обновить схему базы данных +uv run python run.py # uvicorn на 0.0.0.0:8000 с reload +``` + +Или напрямую: + +```bash +uv run uvicorn app.main:app --reload +``` + +После запуска: + +- GraphQL Playground: +- Admin UI: + +### Заполнение демо-данными + + +##### Администратор +Может открыть админ-панель + +Почта: +``` +admin@example.com +``` +Пароль: +``` +Kz8#Fj2!Lm9@Qw4$xR +``` + +##### Пользователь + +``` +user@example.com +``` +``` +DemoUser-Pass#2024 +``` + +##### OAuth-клиент +`demo-client` - "Demo App" +``` +redirect_uris = ["https://example.com/callback"] +``` + +#### Заполнение: + +Убедитесь, что схема создана (запустите один раз или при каждом изменении миграций): + +```bash +uv run alembic upgrade head +``` + +Запустите скрипт заполнения: + +```bash +uv run python seed.py +``` + +Запустите сервер: + +```bash +uv run python run.py +``` + +Админ панель: + + +#### Свои учётные данные + +Переопределите значения по умолчанию через переменные окружения, например: + +```bash +SEED_ADMIN_EMAIL=root@corp.io \ +SEED_ADMIN_PASSWORD='S0me-very-Long-Random-Passphrase!42' \ +uv run python seed.py +``` + +| Переменная | Значение по умолчанию | +| --------------------- | --------------------- | +| `SEED_ADMIN_EMAIL` | `admin@example.com` | +| `SEED_ADMIN_PASSWORD` | `Kz8#Fj2!Lm9@Qw4$xR` | +| `SEED_USER_EMAIL` | `user@example.com` | +| `SEED_USER_PASSWORD` | `DemoUser-Pass#2024` | + +> Пароли из seed-скрипта обходят политику стойкости при регистрации + +#### Сброс + +Чтобы сбросить базу данных: + +```sh +rm -f vault.db +uv run alembic upgrade head +uv run python seed.py +``` + +## Структура проекта + +``` +. +├── alembic/ # окружение миграций Alembic +├── alembic.ini # конфигурация Alembic +├── app/ +│ ├── main.py # FastAPI приложение +│ ├── config.py # настройки из .env +│ ├── db/ # движок, SessionLocal, get_db() и декларативная Base +│ ├── models/ # модели SQLAlchemy: User, OAuthClient, AuditEvent +│ ├── core/ # JWT, хеширование паролей, зависимости аутентификации +│ ├── auth/ # REST и схемы Pydantic +│ ├── audit/ # запись событий в базу данных +│ ├── graphql/ # схема Ariadne, резолверы, контекст и директивы +│ │ └── schema.graphql # SDL GraphQL +│ └── static/ # Фронт +├── tests/ # тесты Pytest +├── run.py # точка входа Uvicorn +├── seed.py # заполнение демо-данными +├── pyproject.toml # метаданные проекта и зависимости (uv) +└── requirements.txt # зафиксированные зависимости для установки через pip +``` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +## Пример GraphQL + +Передайте JWT как bearer-токен: + +```sh +curl http://localhost:8000/graphql/ \ + -H 'Content-Type: application/json' \ + -H 'Authorization: Bearer ' \ + -d '{"query": "{ me { email } }"}' +``` + +Тело запроса: + +```graphql +{ me { email } } +``` + +Пример ответа: + +```json +{ + "data": { + "me": { + "email": "user@example.com" + } + } +} +``` + +## Тестирование + +```bash +uv run pytest -q +``` + +## Примечания + +`.env` и `*.db` не коммитятся - они перечислены в `.gitignore` вместе с `__pycache__/`, `.venv/` и другими локальными артефактами + +Держите секреты и локальные базы данных вне системы контроля версий