diff --git a/.env b/.env index 849493e..1b7816f 100644 --- a/.env +++ b/.env @@ -40,5 +40,5 @@ RECOVERY_MIN_REMAINING_WARNING=3 RECOVERY_MAX_AGE_DAYS=365 # TOTP_2FA -TOTP_ISSUER=LiB +TOTP_ISSUER=LiB-local TOTP_VALID_WINDOW=1 diff --git a/library_service/models/dto/__init__.py b/library_service/models/dto/__init__.py index 8022cbe..e309914 100644 --- a/library_service/models/dto/__init__.py +++ b/library_service/models/dto/__init__.py @@ -7,7 +7,7 @@ from .role import RoleBase, RoleCreate, RoleList, RoleRead, RoleUpdate from .user import UserBase, UserCreate, UserList, UserRead, UserUpdate, UserLogin from .loan import LoanBase, LoanCreate, LoanList, LoanRead, LoanUpdate from .recovery import RecoveryCodesResponse, RecoveryCodesStatus, RecoveryCodeUse -from .token import Token, TokenData, PartialToken +from .token import TokenData from .misc import ( AuthorWithBooks, GenreWithBooks, @@ -62,9 +62,7 @@ __all__ = [ "RoleUpdate", "RoleRead", "RoleList", - "Token", "TokenData", - "PartialToken", "TOTPSetupResponse", "TOTPVerifyRequest", "TOTPDisableRequest", diff --git a/library_service/models/dto/author.py b/library_service/models/dto/author.py index ef7ba85..0386850 100644 --- a/library_service/models/dto/author.py +++ b/library_service/models/dto/author.py @@ -11,8 +11,8 @@ class AuthorBase(SQLModel): name: str = Field(description="Псевдоним") - model_config = ConfigDict( # pyright: ignore - json_schema_extra={"example": {"name": "author_name"}} + model_config = ConfigDict( + json_schema_extra={"example": {"name": "John Doe"}} ) diff --git a/library_service/models/dto/role.py b/library_service/models/dto/role.py index 7efccec..c1abd13 100644 --- a/library_service/models/dto/role.py +++ b/library_service/models/dto/role.py @@ -2,6 +2,7 @@ from typing import List +from pydantic import ConfigDict from sqlmodel import SQLModel, Field @@ -12,6 +13,16 @@ class RoleBase(SQLModel): description: str | None = Field(None, description="Описание") payroll: int = Field(0, description="Оплата") + model_config = ConfigDict( + json_schema_extra={ + "example": { + "name": "admin", + "description": "system administrator", + "payroll": 500, + } + } + ) + class RoleCreate(RoleBase): """Модель роли для создания""" diff --git a/library_service/models/dto/token.py b/library_service/models/dto/token.py index 7c2cb95..33b78da 100644 --- a/library_service/models/dto/token.py +++ b/library_service/models/dto/token.py @@ -1,24 +1,8 @@ -"""Модуль DTO-моделей токенов""" +"""Модуль DTO-модели токена""" from sqlmodel import SQLModel, Field -class Token(SQLModel): - """Модель токена""" - - access_token: str = Field(description="Токен доступа") - token_type: str = Field("bearer", description="Тип токена") - refresh_token: str | None = Field(None, description="Токен обновления") - - -class PartialToken(SQLModel): - """Частичный токен — для подтверждения 2FA""" - - partial_token: str = Field(description="Частичный токен") - token_type: str = Field("partial", description="Тип токена") - requires_2fa: bool = Field(True, description="Требуется TOTP-код") - - class TokenData(SQLModel): """Модель содержимого токена""" diff --git a/library_service/routers/auth.py b/library_service/routers/auth.py index 1c71dd6..5b68ae7 100644 --- a/library_service/routers/auth.py +++ b/library_service/routers/auth.py @@ -12,15 +12,12 @@ from sqlmodel import Session, select from library_service.services import require_captcha from library_service.models.db import Role, User from library_service.models.dto import ( - Token, UserCreate, UserRead, UserUpdate, UserList, RoleRead, RoleList, - Token, - PartialToken, LoginResponse, RecoveryCodeUse, RegisterResponse, @@ -147,11 +144,14 @@ def login( ) access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + new_access_token = create_access_token( + data=token_data, expires_delta=access_token_expires + ) + new_refresh_token = create_refresh_token(data=token_data) + return LoginResponse( - access_token=create_access_token( - data=token_data, expires_delta=access_token_expires - ), - refresh_token=create_refresh_token(data=token_data), + access_token=new_access_token, + refresh_token=new_refresh_token, token_type="bearer", requires_2fa=False, ) @@ -159,7 +159,7 @@ def login( @router.post( "/refresh", - response_model=Token, + response_model=LoginResponse, summary="Обновление токена", description="Получение новой пары токенов, используя действующий Refresh токен", ) @@ -190,19 +190,18 @@ def refresh_token( detail="User is inactive", ) + token_data = {"sub": user.username, "user_id": user.id} access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) new_access_token = create_access_token( - data={"sub": user.username, "user_id": user.id}, - expires_delta=access_token_expires, - ) - new_refresh_token = create_refresh_token( - data={"sub": user.username, "user_id": user.id} + data=token_data, expires_delta=access_token_expires ) + new_refresh_token = create_refresh_token(data=token_data) - return Token( + return LoginResponse( access_token=new_access_token, refresh_token=new_refresh_token, token_type="bearer", + requires_2fa=False, ) @@ -343,7 +342,7 @@ def disable_2fa( @router.post( "/2fa/verify", - response_model=Token, + response_model=LoginResponse, summary="Верификация 2FA", description="Завершает аутентификацию с помощью TOTP кода или резервного кода", ) @@ -374,12 +373,16 @@ def verify_2fa( token_data = {"sub": user.username, "user_id": user.id} access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) + new_access_token = create_access_token( + data=token_data, expires_delta=access_token_expires + ) + new_refresh_token = create_refresh_token(data=token_data) - return Token( - access_token=create_access_token( - data=token_data, expires_delta=access_token_expires - ), - refresh_token=create_refresh_token(data=token_data), + return LoginResponse( + access_token=new_access_token, + refresh_token=new_refresh_token, + token_type="bearer", + requires_2fa=False, ) diff --git a/library_service/static/page/authors.js b/library_service/static/page/authors.js index 5a91efb..634c3fb 100644 --- a/library_service/static/page/authors.js +++ b/library_service/static/page/authors.js @@ -6,6 +6,11 @@ $(document).ready(() => { let currentSort = "name_asc"; loadAuthors(); + const USER_CAN_MANAGE = + typeof window.canManage === "function" && window.canManage(); + if (USER_CAN_MANAGE) { + $("#add-author-btn").removeClass("hidden"); + } function loadAuthors() { showLoadingState(); diff --git a/library_service/templates/authors.html b/library_service/templates/authors.html index 2551120..a03e3a2 100644 --- a/library_service/templates/authors.html +++ b/library_service/templates/authors.html @@ -6,6 +6,12 @@