Fix datetime deprecation warnings in Phase 8 modules
- Update all datetime.utcnow() to datetime.now(UTC) for Python 3.12+ compatibility - Update all datetime.utcfromtimestamp() to datetime.fromtimestamp(..., UTC) - Fix dataclass default_factory to use lambda: datetime.now(UTC) - Update auth, audit, billing, and realtime modules - Add UTC import from datetime module - Update pytest configuration to include pytest-asyncio - All 28 Phase 8 enterprise tests pass with no warnings Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
158
ontology_platform/ont_platform/auth/models.py
Normal file
158
ontology_platform/ont_platform/auth/models.py
Normal file
@@ -0,0 +1,158 @@
|
||||
"""인증 모델 (Organization, User, APIKey).
|
||||
|
||||
Phase 8: 멀티테넌트 지원
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime, UTC
|
||||
from typing import Optional, List
|
||||
import uuid
|
||||
|
||||
|
||||
@dataclass
|
||||
class Organization:
|
||||
"""조직 (테넌트)."""
|
||||
|
||||
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
name: str = ""
|
||||
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
updated_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
is_active: bool = True
|
||||
subscription_tier: str = "free" # "free", "pro", "enterprise"
|
||||
max_users: int = 5 # Free tier 기본값
|
||||
storage_limit_gb: int = 1
|
||||
api_quota_monthly: int = 10000
|
||||
metadata: dict = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""딕셔너리로 변환."""
|
||||
return {
|
||||
"id": self.id,
|
||||
"name": self.name,
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"is_active": self.is_active,
|
||||
"subscription_tier": self.subscription_tier,
|
||||
"max_users": self.max_users,
|
||||
"storage_limit_gb": self.storage_limit_gb,
|
||||
"api_quota_monthly": self.api_quota_monthly,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class User:
|
||||
"""사용자."""
|
||||
|
||||
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
org_id: str = "" # 조직 ID (FK)
|
||||
email: str = ""
|
||||
username: str = ""
|
||||
hashed_password: str = ""
|
||||
role: str = "viewer" # "admin", "editor", "viewer"
|
||||
is_active: bool = True
|
||||
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
updated_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
last_login: Optional[datetime] = None
|
||||
metadata: dict = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""딕셔너리로 변환."""
|
||||
return {
|
||||
"id": self.id,
|
||||
"org_id": self.org_id,
|
||||
"email": self.email,
|
||||
"username": self.username,
|
||||
"role": self.role,
|
||||
"is_active": self.is_active,
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"last_login": self.last_login.isoformat() if self.last_login else None,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class APIKey:
|
||||
"""API 키."""
|
||||
|
||||
id: str = field(default_factory=lambda: str(uuid.uuid4()))
|
||||
org_id: str = "" # 조직 ID (FK)
|
||||
key_hash: str = "" # SHA256 해시 (원본은 저장하지 않음)
|
||||
name: str = ""
|
||||
description: str = ""
|
||||
is_active: bool = True
|
||||
created_at: datetime = field(default_factory=lambda: datetime.now(UTC))
|
||||
last_used: Optional[datetime] = None
|
||||
last_used_ip: Optional[str] = None
|
||||
metadata: dict = field(default_factory=dict)
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""딕셔너리로 변환 (해시만 포함)."""
|
||||
return {
|
||||
"id": self.id,
|
||||
"org_id": self.org_id,
|
||||
"name": self.name,
|
||||
"description": self.description,
|
||||
"is_active": self.is_active,
|
||||
"created_at": self.created_at.isoformat(),
|
||||
"last_used": self.last_used.isoformat() if self.last_used else None,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class TokenPayload:
|
||||
"""JWT 토큰 페이로드."""
|
||||
|
||||
user_id: str
|
||||
org_id: str
|
||||
email: str
|
||||
role: str
|
||||
exp: int # Unix timestamp
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""딕셔너리로 변환."""
|
||||
return {
|
||||
"user_id": self.user_id,
|
||||
"org_id": self.org_id,
|
||||
"email": self.email,
|
||||
"role": self.role,
|
||||
"exp": self.exp,
|
||||
}
|
||||
|
||||
|
||||
@dataclass
|
||||
class AuthCredentials:
|
||||
"""인증 자격증명."""
|
||||
|
||||
email: Optional[str] = None
|
||||
password: Optional[str] = None
|
||||
api_key: Optional[str] = None
|
||||
token: Optional[str] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class CurrentUser:
|
||||
"""현재 인증된 사용자."""
|
||||
|
||||
user_id: str
|
||||
org_id: str
|
||||
email: str
|
||||
username: str
|
||||
role: str
|
||||
is_active: bool
|
||||
|
||||
def has_permission(self, action: str) -> bool:
|
||||
"""사용자가 작업 권한을 가지고 있는지 확인."""
|
||||
from ont_platform.auth.rbac import RBAC
|
||||
|
||||
rbac = RBAC()
|
||||
permissions = rbac.get_permissions(self.role)
|
||||
return action in permissions
|
||||
|
||||
def to_dict(self) -> dict:
|
||||
"""딕셔너리로 변환."""
|
||||
return {
|
||||
"user_id": self.user_id,
|
||||
"org_id": self.org_id,
|
||||
"email": self.email,
|
||||
"username": self.username,
|
||||
"role": self.role,
|
||||
"is_active": self.is_active,
|
||||
}
|
||||
Reference in New Issue
Block a user