from sqlalchemy.orm import Session from sqlalchemy.exc import IntegrityError from fastapi import HTTPException, status from app.models.user import User, UserRole from app.models.tenant import Organization from app.schemas.auth import RegisterRequest, LoginRequest from app.core.security import verify_password, get_password_hash, create_access_token from app.services.audit import AuditService from typing import Optional class AuthService: def __init__(self, db: Session): self.db = db self.audit_service = AuditService(db) def authenticate_user(self, email: str, password: str) -> Optional[User]: user = self.db.query(User).filter(User.email == email).first() if not user: return None if not verify_password(password, user.hashed_password): return None return user def login(self, login_data: LoginRequest, ip_address: str, user_agent: str): user = self.authenticate_user(login_data.email, login_data.password) if not user: raise HTTPException( status_code=status.HTTP_401_UNAUTHORIZED, detail="Incorrect email or password", headers={"WWW-Authenticate": "Bearer"}, ) if not user.is_active: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Inactive user" ) access_token = create_access_token(data={"sub": str(user.id)}) # Log login activity self.audit_service.log_user_activity( user=user, action="login", resource_type="authentication", ip_address=ip_address, user_agent=user_agent ) # Update last login from datetime import datetime user.last_login = datetime.utcnow() self.db.commit() return {"access_token": access_token, "token_type": "bearer"} def register(self, register_data: RegisterRequest, ip_address: str, user_agent: str): try: # Create organization first organization = Organization( name=register_data.organization_name, domain=register_data.organization_domain, subdomain=register_data.organization_subdomain ) self.db.add(organization) self.db.flush() # Get the ID without committing # Create user as org admin hashed_password = get_password_hash(register_data.password) user = User( email=register_data.email, username=register_data.username, hashed_password=hashed_password, first_name=register_data.first_name, last_name=register_data.last_name, role=UserRole.ORG_ADMIN, organization_id=organization.id, is_verified=True # Auto-verify for demo ) self.db.add(user) self.db.commit() # Log registration self.audit_service.log_action( organization_id=organization.id, user_id=user.id, action="create", resource_type="user_registration", resource_id=str(user.id), details={"role": user.role.value}, ip_address=ip_address, user_agent=user_agent ) return user except IntegrityError as e: self.db.rollback() if "email" in str(e.orig): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Email already registered" ) elif "username" in str(e.orig): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Username already taken" ) elif "domain" in str(e.orig): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Domain already registered" ) elif "subdomain" in str(e.orig): raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Subdomain already taken" ) else: raise HTTPException( status_code=status.HTTP_400_BAD_REQUEST, detail="Registration failed" )