Add subcription feature 3
This commit is contained in:
@@ -87,7 +87,7 @@ async def get_db_health() -> bool:
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Database health check failed", error=str(e))
|
||||
logger.error(f"Database health check failed: {str(e)}")
|
||||
return False
|
||||
|
||||
async def create_tables():
|
||||
@@ -173,7 +173,7 @@ class AuthDatabaseUtils:
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to get auth statistics", error=str(e))
|
||||
logger.error(f"Failed to get auth statistics: {str(e)}")
|
||||
return {
|
||||
"active_users": 0,
|
||||
"inactive_users": 0,
|
||||
@@ -234,7 +234,7 @@ async def get_db_session() -> AsyncGenerator[AsyncSession, None]:
|
||||
logger.debug("Database session created")
|
||||
yield session
|
||||
except Exception as e:
|
||||
logger.error("Database session error", error=str(e), exc_info=True)
|
||||
logger.error(f"Database session error: {str(e)}", exc_info=True)
|
||||
await session.rollback()
|
||||
raise
|
||||
finally:
|
||||
@@ -257,7 +257,7 @@ async def initialize_auth_database():
|
||||
logger.info("Auth service database initialized successfully")
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to initialize auth service database", error=str(e))
|
||||
logger.error(f"Failed to initialize auth service database: {str(e)}")
|
||||
raise
|
||||
|
||||
# Database cleanup for auth service
|
||||
@@ -273,7 +273,7 @@ async def cleanup_auth_database():
|
||||
logger.info("Auth service database cleanup completed")
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Failed to cleanup auth service database", error=str(e))
|
||||
logger.error(f"Failed to cleanup auth service database: {str(e)}")
|
||||
|
||||
# Export the commonly used items to maintain compatibility
|
||||
__all__ = [
|
||||
|
||||
@@ -303,8 +303,9 @@ class SecurityManager:
|
||||
async def track_login_attempt(email: str, ip_address: str, success: bool) -> None:
|
||||
"""Track login attempts for security monitoring"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
key = f"login_attempts:{email}:{ip_address}"
|
||||
|
||||
|
||||
if success:
|
||||
# Clear failed attempts on successful login
|
||||
await redis_client.delete(key)
|
||||
@@ -314,14 +315,16 @@ class SecurityManager:
|
||||
if attempts == 1:
|
||||
# Set expiration on first failed attempt
|
||||
await redis_client.expire(key, settings.LOCKOUT_DURATION_MINUTES * 60)
|
||||
|
||||
|
||||
if attempts >= settings.MAX_LOGIN_ATTEMPTS:
|
||||
logger.warning(f"Account locked for {email} from {ip_address}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_429_TOO_MANY_REQUESTS,
|
||||
detail=f"Too many failed login attempts. Try again in {settings.LOCKOUT_DURATION_MINUTES} minutes."
|
||||
)
|
||||
|
||||
|
||||
except HTTPException:
|
||||
raise # Re-raise HTTPException
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to track login attempt: {e}")
|
||||
|
||||
@@ -329,16 +332,17 @@ class SecurityManager:
|
||||
async def is_account_locked(email: str, ip_address: str) -> bool:
|
||||
"""Check if account is locked due to failed login attempts"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
key = f"login_attempts:{email}:{ip_address}"
|
||||
attempts = await redis_client.get(key)
|
||||
|
||||
|
||||
if attempts:
|
||||
attempts = int(attempts)
|
||||
return attempts >= settings.MAX_LOGIN_ATTEMPTS
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to check account lock status: {e}")
|
||||
|
||||
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
@@ -364,31 +368,34 @@ class SecurityManager:
|
||||
async def check_login_attempts(email: str) -> bool:
|
||||
"""Check if user has exceeded login attempts"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
key = f"login_attempts:{email}"
|
||||
attempts = await redis_client.get(key)
|
||||
|
||||
|
||||
if attempts is None:
|
||||
return True
|
||||
|
||||
|
||||
return int(attempts) < settings.MAX_LOGIN_ATTEMPTS
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking login attempts: {e}")
|
||||
return True # Allow on error
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def increment_login_attempts(email: str) -> None:
|
||||
"""Increment login attempts for email"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
key = f"login_attempts:{email}"
|
||||
await redis_client.incr(key)
|
||||
await redis_client.expire(key, settings.LOCKOUT_DURATION_MINUTES * 60)
|
||||
except Exception as e:
|
||||
logger.error(f"Error incrementing login attempts: {e}")
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def clear_login_attempts(email: str) -> None:
|
||||
"""Clear login attempts for email after successful login"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
key = f"login_attempts:{email}"
|
||||
await redis_client.delete(key)
|
||||
logger.debug(f"Cleared login attempts for {email}")
|
||||
@@ -399,39 +406,42 @@ class SecurityManager:
|
||||
async def store_refresh_token(user_id: str, token: str) -> None:
|
||||
"""Store refresh token in Redis"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
token_hash = SecurityManager.hash_api_key(token) # Reuse hash method
|
||||
key = f"refresh_token:{user_id}:{token_hash}"
|
||||
|
||||
|
||||
# Store with expiration matching JWT refresh token expiry
|
||||
expire_seconds = settings.JWT_REFRESH_TOKEN_EXPIRE_DAYS * 24 * 60 * 60
|
||||
await redis_client.setex(key, expire_seconds, "valid")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error storing refresh token: {e}")
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def is_refresh_token_valid(user_id: str, token: str) -> bool:
|
||||
"""Check if refresh token is still valid in Redis"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
token_hash = SecurityManager.hash_api_key(token)
|
||||
key = f"refresh_token:{user_id}:{token_hash}"
|
||||
|
||||
|
||||
exists = await redis_client.exists(key)
|
||||
return bool(exists)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error checking refresh token validity: {e}")
|
||||
return False
|
||||
|
||||
|
||||
@staticmethod
|
||||
async def revoke_refresh_token(user_id: str, token: str) -> None:
|
||||
"""Revoke refresh token by removing from Redis"""
|
||||
try:
|
||||
redis_client = await get_redis_client()
|
||||
token_hash = SecurityManager.hash_api_key(token)
|
||||
key = f"refresh_token:{user_id}:{token_hash}"
|
||||
|
||||
|
||||
await redis_client.delete(key)
|
||||
logger.debug(f"Revoked refresh token for user {user_id}")
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error revoking refresh token: {e}")
|
||||
Reference in New Issue
Block a user