86 lines
3.2 KiB
Python
86 lines
3.2 KiB
Python
from __future__ import annotations
|
||
|
||
from cryptography.fernet import Fernet, InvalidToken
|
||
from sqlalchemy import select
|
||
|
||
from app.core.config import settings
|
||
from app.db.base import SessionLocal
|
||
from app.db.models.opencloud_token import OpenCloudToken
|
||
|
||
|
||
class OpenCloudTokenStoreError(RuntimeError):
|
||
"""Ошибка при работе с таблицей токенов OpenCloud (шифрование/БД)."""
|
||
|
||
|
||
def _get_fernet() -> Fernet:
|
||
if not settings.FERNET_KEY:
|
||
raise OpenCloudTokenStoreError(
|
||
"Не задан OPENBAO__SETTINGS__FERNET_KEY — шифрование токенов "
|
||
"OpenCloud недоступно."
|
||
)
|
||
try:
|
||
return Fernet(settings.FERNET_KEY.encode())
|
||
except (ValueError, TypeError) as exc:
|
||
raise OpenCloudTokenStoreError(
|
||
f"OPENBAO__SETTINGS__FERNET_KEY некорректен: {exc}"
|
||
) from exc
|
||
|
||
|
||
async def add_token(username: str, token: str, added_by: str | None = None) -> None:
|
||
"""Сохраняет (или перезаписывает) App Token заказчика, зашифровав его."""
|
||
if not username:
|
||
raise OpenCloudTokenStoreError("Не задан username для сохранения токена.")
|
||
if not token:
|
||
raise OpenCloudTokenStoreError("Не задан токен для сохранения.")
|
||
|
||
encrypted = _get_fernet().encrypt(token.encode()).decode()
|
||
|
||
async with SessionLocal() as session:
|
||
obj = await session.get(OpenCloudToken, username)
|
||
if obj is not None:
|
||
obj.encrypted_token = encrypted
|
||
if added_by:
|
||
obj.added_by = added_by
|
||
else:
|
||
session.add(
|
||
OpenCloudToken(
|
||
username=username, encrypted_token=encrypted, added_by=added_by
|
||
)
|
||
)
|
||
await session.commit()
|
||
|
||
|
||
async def get_token(username: str) -> str | None:
|
||
"""username -> расшифрованный App Token, либо None, если записи нет."""
|
||
if not username:
|
||
return None
|
||
async with SessionLocal() as session:
|
||
obj = await session.get(OpenCloudToken, username)
|
||
if obj is None:
|
||
return None
|
||
try:
|
||
return _get_fernet().decrypt(obj.encrypted_token.encode()).decode()
|
||
except InvalidToken as exc:
|
||
raise OpenCloudTokenStoreError(
|
||
f"Не удалось расшифровать токен для '{username}' — "
|
||
"проверьте OPENBAO__SETTINGS__FERNET_KEY."
|
||
) from exc
|
||
|
||
|
||
async def remove_token(username: str) -> bool:
|
||
"""Удаляет запись пользователя. Возвращает True, если запись была найдена."""
|
||
async with SessionLocal() as session:
|
||
obj = await session.get(OpenCloudToken, username)
|
||
if obj is None:
|
||
return False
|
||
await session.delete(obj)
|
||
await session.commit()
|
||
return True
|
||
|
||
|
||
async def list_usernames() -> list[str]:
|
||
"""Список username, для которых сохранён токен (без расшифровки)."""
|
||
async with SessionLocal() as session:
|
||
result = await session.execute(select(OpenCloudToken.username))
|
||
return list(result.scalars().all())
|