Files
ipam_control/app/security.py
T
ayurishchevandClaude Sonnet 5 a846d30872 IPAM Manager: API, UI-админка, журнал аудита
Backend (FastAPI, SQLAlchemy 2, Alembic, PostgreSQL 16):
- организации, VRF, префиксы (дерево, использование, автоназначение),
  адреса, операторы связи, устройства и типы устройств; JWT, роли admin/viewer;
- VRF принадлежит организации (составной FK), смена VRF у префикса
  переносит поддерево, имя VRF уникально в организации;
- журнал аудита: поиск и фильтры, ротация (срок/количество), очистка
  по паролю с блокировкой, IP клиента и метаданные запроса
  (X-Forwarded-For только от TRUSTED_PROXIES).

UI (web/, без сборки): экраны и диалоги по макетам «IPAM Manager»,
кликабельные строки реестров, локальные шрифты IBM Plex,
собственные выпадающие списки.

Окружение: docker-compose (postgres + app), миграции Alembic 0001-0004,
scripts/gen_env.py, scripts/seed_demo.py, 11 автотестов (pytest).
Документация: README.md и docs/changes/001-005 (планы и итоги).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-20 12:27:47 +03:00

54 lines
1.7 KiB
Python

from datetime import datetime, timedelta, timezone
import jwt
from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError
from fastapi import Depends, HTTPException
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from sqlalchemy import select
from sqlalchemy.orm import Session
from app.config import settings
from app.db import get_db
from app.models import Role, User
_ph = PasswordHasher()
_bearer = HTTPBearer(auto_error=False)
def hash_password(password: str) -> str:
return _ph.hash(password)
def verify_password(password: str, hashed: str) -> bool:
try:
return _ph.verify(hashed, password)
except VerifyMismatchError:
return False
def create_token(user: User) -> str:
exp = datetime.now(timezone.utc) + timedelta(minutes=settings.jwt_ttl_minutes)
return jwt.encode({"sub": user.username, "exp": exp}, settings.jwt_secret, algorithm="HS256")
def current_user(
cred: HTTPAuthorizationCredentials | None = Depends(_bearer), db: Session = Depends(get_db)
) -> User:
if cred is None:
raise HTTPException(401, "Требуется авторизация")
try:
username = jwt.decode(cred.credentials, settings.jwt_secret, algorithms=["HS256"])["sub"]
except jwt.PyJWTError:
raise HTTPException(401, "Недействительный токен")
user = db.scalar(select(User).where(User.username == username, User.is_active))
if user is None:
raise HTTPException(401, "Пользователь не найден")
return user
def admin_user(user: User = Depends(current_user)) -> User:
if user.role != Role.admin:
raise HTTPException(403, "Недостаточно прав")
return user