auth-wip
This commit is contained in:
@@ -91,24 +91,8 @@ class Identity:
|
||||
)
|
||||
raise InvalidPassword("Пароль не установлен для данного пользователя")
|
||||
|
||||
# Проверим словарь до создания нового объекта
|
||||
author_dict = orm_author.dict()
|
||||
if "password" not in author_dict or not author_dict["password"]:
|
||||
logger.warning(
|
||||
f"[auth.identity] Пароль отсутствует в dict() или пуст: email={orm_author.email}"
|
||||
)
|
||||
raise InvalidPassword("Пароль отсутствует в данных пользователя")
|
||||
|
||||
# Создаем новый объект автора
|
||||
author = Author(**author_dict)
|
||||
if not author.password:
|
||||
logger.warning(
|
||||
f"[auth.identity] Пароль в созданном объекте автора пуст: email={orm_author.email}"
|
||||
)
|
||||
raise InvalidPassword("Пароль не установлен для данного пользователя")
|
||||
|
||||
# Проверяем пароль
|
||||
if not Password.verify(password, author.password):
|
||||
# Проверяем пароль напрямую, не используя dict()
|
||||
if not Password.verify(password, orm_author.password):
|
||||
logger.warning(f"[auth.identity] Неверный пароль для {orm_author.email}")
|
||||
raise InvalidPassword("Неверный пароль пользователя")
|
||||
|
||||
|
@@ -151,16 +151,16 @@ class InternalAuthentication(AuthenticationBackend):
|
||||
return AuthCredentials(scopes={}, error_message="User not found"), UnauthenticatedUser()
|
||||
|
||||
|
||||
async def verify_internal_auth(token: str) -> Tuple[str, list]:
|
||||
async def verify_internal_auth(token: str) -> Tuple[str, list, bool]:
|
||||
"""
|
||||
Проверяет локальную авторизацию.
|
||||
Возвращает user_id и список ролей.
|
||||
Возвращает user_id, список ролей и флаг администратора.
|
||||
|
||||
Args:
|
||||
token: Токен авторизации (может быть как с Bearer, так и без)
|
||||
|
||||
Returns:
|
||||
tuple: (user_id, roles)
|
||||
tuple: (user_id, roles, is_admin)
|
||||
"""
|
||||
# Обработка формата "Bearer <token>" (если токен не был обработан ранее)
|
||||
if token.startswith("Bearer "):
|
||||
@@ -169,7 +169,7 @@ async def verify_internal_auth(token: str) -> Tuple[str, list]:
|
||||
# Проверяем сессию
|
||||
payload = await SessionManager.verify_session(token)
|
||||
if not payload:
|
||||
return "", []
|
||||
return "", [], False
|
||||
|
||||
with local_session() as session:
|
||||
try:
|
||||
@@ -182,10 +182,13 @@ async def verify_internal_auth(token: str) -> Tuple[str, list]:
|
||||
|
||||
# Получаем роли
|
||||
roles = [role.id for role in author.roles]
|
||||
|
||||
return str(author.id), roles
|
||||
|
||||
# Определяем, является ли пользователь администратором
|
||||
is_admin = any(role in ['admin', 'super'] for role in roles) or author.email in ADMIN_EMAILS
|
||||
|
||||
return str(author.id), roles, is_admin
|
||||
except exc.NoResultFound:
|
||||
return "", []
|
||||
return "", [], False
|
||||
|
||||
|
||||
async def create_internal_session(author: Author, device_info: Optional[dict] = None) -> str:
|
||||
@@ -202,8 +205,8 @@ async def create_internal_session(author: Author, device_info: Optional[dict] =
|
||||
# Сбрасываем счетчик неудачных попыток
|
||||
author.reset_failed_login()
|
||||
|
||||
# Обновляем last_login
|
||||
author.last_login = int(time.time())
|
||||
# Обновляем last_seen
|
||||
author.last_seen = int(time.time())
|
||||
|
||||
# Создаем сессию, используя token для идентификации
|
||||
return await SessionManager.create_session(
|
||||
|
48
auth/orm.py
48
auth/orm.py
@@ -5,6 +5,7 @@ from sqlalchemy.orm import relationship
|
||||
|
||||
from auth.identity import Password
|
||||
from services.db import Base
|
||||
from settings import ADMIN_EMAILS
|
||||
|
||||
# from sqlalchemy_utils import TSVectorType
|
||||
|
||||
@@ -165,7 +166,6 @@ class Author(Base):
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
email_verified = Column(Boolean, default=False)
|
||||
phone_verified = Column(Boolean, default=False)
|
||||
last_login = Column(Integer, nullable=True)
|
||||
failed_login_attempts = Column(Integer, default=0)
|
||||
account_locked_until = Column(Integer, nullable=True)
|
||||
|
||||
@@ -182,6 +182,9 @@ class Author(Base):
|
||||
# TSVectorType("name", "slug", "bio", "about", regconfig="pg_catalog.russian")
|
||||
# )
|
||||
|
||||
# Список защищенных полей, которые видны только владельцу и администраторам
|
||||
_protected_fields = ['email', 'password', 'provider_access_token', 'provider_refresh_token']
|
||||
|
||||
@property
|
||||
def is_authenticated(self) -> bool:
|
||||
"""Проверяет, аутентифицирован ли пользователь"""
|
||||
@@ -238,22 +241,27 @@ class Author(Base):
|
||||
"""
|
||||
return self.slug or self.email or self.phone or ""
|
||||
|
||||
def dict(self) -> Dict:
|
||||
"""Преобразует объект Author в словарь"""
|
||||
return {
|
||||
"id": self.id,
|
||||
"slug": self.slug,
|
||||
"name": self.name,
|
||||
"bio": self.bio,
|
||||
"about": self.about,
|
||||
"pic": self.pic,
|
||||
"links": self.links,
|
||||
"email": self.email,
|
||||
"password": self.password,
|
||||
"created_at": self.created_at,
|
||||
"updated_at": self.updated_at,
|
||||
"last_seen": self.last_seen,
|
||||
"deleted_at": self.deleted_at,
|
||||
"roles": [role.id for role in self.roles],
|
||||
"email_verified": self.email_verified,
|
||||
}
|
||||
def dict(self, access=False) -> Dict:
|
||||
"""
|
||||
Сериализует объект Author в словарь с учетом прав доступа.
|
||||
|
||||
Args:
|
||||
access (bool, optional): Флаг, указывающий, доступны ли защищенные поля
|
||||
|
||||
Returns:
|
||||
dict: Словарь с атрибутами Author, отфильтрованный по правам доступа
|
||||
"""
|
||||
# Получаем все атрибуты объекта
|
||||
result = {c.name: getattr(self, c.name) for c in self.__table__.columns}
|
||||
|
||||
# Добавляем роли, если они есть
|
||||
if hasattr(self, 'roles') and self.roles:
|
||||
result['roles'] = [role.id for role in self.roles]
|
||||
|
||||
# скрываем защищенные поля
|
||||
if not access:
|
||||
for field in self._protected_fields:
|
||||
if field in result:
|
||||
result[field] = None
|
||||
|
||||
return result
|
||||
|
Reference in New Issue
Block a user