Spaces:
Sleeping
Sleeping
| import logging | |
| from typing import List, Optional | |
| from fastapi import Depends, Request | |
| from pydantic import BaseModel | |
| import pyotp | |
| from sqlalchemy.orm import Session | |
| from core.audit_service import audit_service | |
| from core.auth import get_current_user | |
| from core.base_routes import BaseAPIRouter | |
| from core.database import get_db | |
| from core.models import AuditEventType, SecurityLevel, ThreatLevel, User | |
| logger = logging.getLogger(__name__) | |
| router = BaseAPIRouter(prefix="/api/auth/2fa", tags=["Authentication-2FA"]) | |
| class TwoFactorSetupResponse(BaseModel): | |
| secret: str | |
| otpauth_url: str | |
| class TwoFactorVerifyRequest(BaseModel): | |
| code: str | |
| class TwoFactorStatusResponse(BaseModel): | |
| enabled: bool | |
| async def get_2fa_status(current_user: User = Depends(get_current_user)): | |
| """Check if 2FA is enabled for the current user""" | |
| return {"enabled": current_user.two_factor_enabled} | |
| async def setup_2fa(current_user: User = Depends(get_current_user), db: Session = Depends(get_db)): | |
| """Generate a new 2FA secret and provisioning URL""" | |
| if current_user.two_factor_enabled: | |
| raise router.conflict_error("2FA is already enabled") | |
| secret = pyotp.random_base32() | |
| issuer_name = "Atom AI (Upstream)" | |
| otpauth_url = pyotp.totp.TOTP(secret).provisioning_uri( | |
| name=current_user.email, | |
| issuer_name=issuer_name | |
| ) | |
| current_user.two_factor_secret = secret | |
| db.commit() | |
| return { | |
| "secret": secret, | |
| "otpauth_url": otpauth_url | |
| } | |
| async def enable_2fa( | |
| request: Request, | |
| verify_data: TwoFactorVerifyRequest, | |
| current_user: User = Depends(get_current_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Verify code and enable 2FA""" | |
| if current_user.two_factor_enabled: | |
| raise router.conflict_error("2FA is already enabled") | |
| if not current_user.two_factor_secret: | |
| raise router.validation_error("two_factor_secret", "2FA setup not initiated") | |
| totp = pyotp.TOTP(current_user.two_factor_secret) | |
| if totp.verify(verify_data.code): | |
| current_user.two_factor_enabled = True | |
| current_user.two_factor_backup_codes = ["UP-BACKUP-1234-5678"] | |
| db.commit() | |
| audit_service.log_event( | |
| db, | |
| event_type=AuditEventType.UPDATE.value, | |
| action="2fa_enabled", | |
| description=f"2FA enabled for user: {current_user.email}", | |
| user_id=current_user.id, | |
| user_email=current_user.email, | |
| security_level=SecurityLevel.HIGH.value, | |
| request=request | |
| ) | |
| return router.success_response( | |
| data={"backup_codes": current_user.two_factor_backup_codes}, | |
| message="2FA enabled successfully" | |
| ) | |
| else: | |
| raise router.validation_error("code", "Invalid verification code") | |
| async def disable_2fa( | |
| request: Request, | |
| verify_data: TwoFactorVerifyRequest, | |
| current_user: User = Depends(get_current_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Disable 2FA after verifying a code""" | |
| if not current_user.two_factor_enabled: | |
| raise router.validation_error("two_factor_enabled", "2FA is not enabled") | |
| totp = pyotp.TOTP(current_user.two_factor_secret) | |
| if totp.verify(verify_data.code): | |
| current_user.two_factor_enabled = False | |
| current_user.two_factor_secret = None | |
| current_user.two_factor_backup_codes = None | |
| db.commit() | |
| audit_service.log_event( | |
| db, | |
| event_type=AuditEventType.UPDATE.value, | |
| action="2fa_disabled", | |
| description=f"2FA disabled for user: {current_user.email}", | |
| user_id=current_user.id, | |
| user_email=current_user.email, | |
| security_level=SecurityLevel.HIGH.value, | |
| request=request | |
| ) | |
| return router.success_response(message="2FA disabled successfully") | |
| else: | |
| raise router.validation_error("code", "Invalid verification code") | |
| class Action2FAVerifyRequest(BaseModel): | |
| code: str | |
| async def verify_action_2fa( | |
| action_id: str, | |
| verify_data: Action2FAVerifyRequest, | |
| current_user: User = Depends(get_current_user), | |
| db: Session = Depends(get_db) | |
| ): | |
| """Verify 2FA code and resolve a high-stakes HITL action""" | |
| if not current_user.two_factor_enabled: | |
| raise router.validation_error("two_factor_enabled", "2FA is not enabled for your account") | |
| # 1. Verify TOTP Code | |
| import pyotp | |
| totp = pyotp.TOTP(current_user.two_factor_secret) | |
| if not totp.verify(verify_data.code): | |
| raise router.validation_error("code", "Invalid verification code") | |
| # 2. Resolve Action via HITLService | |
| from core.hitl_service import hitl_service | |
| try: | |
| result = await hitl_service.resolve_action( | |
| action_id=action_id, | |
| resolution="approved", | |
| resolver_id=current_user.id, | |
| metadata={"verified_2fa": True} | |
| ) | |
| # Log to Audit | |
| from core.models import AuditEventType, SecurityLevel | |
| audit_service.log_event( | |
| db, | |
| event_type=AuditEventType.UPDATE.value, | |
| action="hitl_action_verified_2fa", | |
| description=f"HITL Action {action_id} approved via 2FA by {current_user.email}", | |
| user_id=current_user.id, | |
| user_email=current_user.email, | |
| security_level=SecurityLevel.HIGH.value | |
| ) | |
| return router.success_response(data=result, message="Action approved successfully via 2FA") | |
| except Exception as e: | |
| logger.error(f"Failed to verify action via 2FA: {e}") | |
| raise router.server_error(f"Failed to resolve action: {str(e)}") | |