feat(auth): owner-bound bot tokens and self-service bot management
This commit is contained in:
@@ -13,7 +13,13 @@ from app.core.security import generate_token, hash_token, token_prefix, verify_p
|
||||
from app.domain.models import AuthToken, BotPrincipal, User
|
||||
from app.repositories.factory import get_portfolio_repository_from_settings
|
||||
from app.repositories.portfolio_repository import PortfolioRepository
|
||||
from app.schemas.auth import CreateBotPrincipalRequest, CreateBotTokenRequest, CreateClientTokenRequest, LoginRequest
|
||||
from app.schemas.auth import (
|
||||
CreateBotPrincipalRequest,
|
||||
CreateBotTokenRequest,
|
||||
CreateClientTokenRequest,
|
||||
CreateSelfBotPrincipalRequest,
|
||||
LoginRequest,
|
||||
)
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -105,6 +111,16 @@ class AuthService:
|
||||
def create_bot_principal(self, payload: CreateBotPrincipalRequest) -> BotPrincipal:
|
||||
return self._repository.create_bot_principal(
|
||||
name=payload.name,
|
||||
owner_user_id=payload.owner_user_id,
|
||||
permission=payload.permission,
|
||||
description=payload.description,
|
||||
)
|
||||
|
||||
def create_owned_bot_principal(self, auth_context: AuthContext, payload: CreateSelfBotPrincipalRequest) -> BotPrincipal:
|
||||
user_id = self._require_db_user_context(auth_context)
|
||||
return self._repository.create_bot_principal(
|
||||
name=payload.name,
|
||||
owner_user_id=user_id,
|
||||
permission=payload.permission,
|
||||
description=payload.description,
|
||||
)
|
||||
@@ -112,6 +128,10 @@ class AuthService:
|
||||
def list_bot_principals(self, *, is_active: bool | None = None) -> list[BotPrincipal]:
|
||||
return self._repository.list_bot_principals(is_active=is_active)
|
||||
|
||||
def list_owned_bot_principals(self, auth_context: AuthContext, *, is_active: bool | None = None) -> list[BotPrincipal]:
|
||||
user_id = self._require_db_user_context(auth_context)
|
||||
return self._repository.list_bot_principals(is_active=is_active, owner_user_id=user_id)
|
||||
|
||||
def get_bot_principal(self, bot_principal_id: str) -> BotPrincipal:
|
||||
return self._repository.get_bot_principal(bot_principal_id)
|
||||
|
||||
@@ -142,14 +162,48 @@ class AuthService:
|
||||
)
|
||||
return IssuedBotToken(token=raw_token, auth_token=auth_token)
|
||||
|
||||
def issue_owned_bot_token(
|
||||
self,
|
||||
auth_context: AuthContext,
|
||||
bot_principal_id: str,
|
||||
payload: CreateBotTokenRequest,
|
||||
) -> IssuedBotToken:
|
||||
user_id = self._require_db_user_context(auth_context)
|
||||
bot = self._repository.get_bot_principal(bot_principal_id)
|
||||
if bot.owner_user_id != user_id:
|
||||
raise ValueError("봇 접근 권한이 없습니다")
|
||||
return self.issue_bot_token(bot_principal_id, payload)
|
||||
|
||||
def list_bot_tokens(self, bot_principal_id: str) -> list[AuthToken]:
|
||||
self._repository.get_bot_principal(bot_principal_id)
|
||||
return self._repository.list_bot_auth_tokens(bot_principal_id)
|
||||
|
||||
def list_owned_bot_tokens(self, auth_context: AuthContext, bot_principal_id: str) -> list[AuthToken]:
|
||||
user_id = self._require_db_user_context(auth_context)
|
||||
bot = self._repository.get_bot_principal(bot_principal_id)
|
||||
if bot.owner_user_id != user_id:
|
||||
raise ValueError("봇 접근 권한이 없습니다")
|
||||
return self._repository.list_bot_auth_tokens(bot_principal_id)
|
||||
|
||||
def revoke_bot_token(self, bot_principal_id: str, token_id: str) -> None:
|
||||
self._repository.get_bot_principal(bot_principal_id)
|
||||
self._repository.revoke_bot_auth_token(bot_principal_id, token_id)
|
||||
|
||||
def revoke_owned_bot_token(self, auth_context: AuthContext, bot_principal_id: str, token_id: str) -> None:
|
||||
user_id = self._require_db_user_context(auth_context)
|
||||
bot = self._repository.get_bot_principal(bot_principal_id)
|
||||
if bot.owner_user_id != user_id:
|
||||
raise ValueError("봇 접근 권한이 없습니다")
|
||||
self._repository.revoke_bot_auth_token(bot_principal_id, token_id)
|
||||
|
||||
@staticmethod
|
||||
def _require_db_user_context(auth_context: AuthContext) -> str:
|
||||
if auth_context.bot_principal_id is not None:
|
||||
raise ValueError("봇 토큰으로는 사용자 전용 봇 관리를 수행할 수 없습니다")
|
||||
if auth_context.user_id is None:
|
||||
raise ValueError("DB 사용자 컨텍스트에서만 사용자 전용 봇 관리를 수행할 수 있습니다")
|
||||
return auth_context.user_id
|
||||
|
||||
@staticmethod
|
||||
def _build_expiration(expires_in_days: int | None, default_days: int) -> datetime:
|
||||
days = expires_in_days if expires_in_days is not None else default_days
|
||||
|
||||
Reference in New Issue
Block a user