# /opt/docker/dev/service_finder/backend/app/services/security_service.py import logging from datetime import datetime, timedelta, timezone from typing import Optional, Any, Dict from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select, func, and_ from app.models.security import PendingAction, ActionStatus from app.models.history import AuditLog, LogSeverity from app.models.identity import User from app.models.system import SystemParameter logger = logging.getLogger(__name__) class SecurityService: @staticmethod async def get_sec_config(db: AsyncSession) -> Dict[str, Any]: """ Lekéri a korlátokat a központi system_parameters-ből. """ stmt = select(SystemParameter).where(SystemParameter.key.in_(["SECURITY_MAX_RECORDS_PER_HOUR", "SECURITY_DUAL_CONTROL_ENABLED"])) res = await db.execute(stmt) params = {p.key: p.value for p in res.scalars().all()} return { "max_records": int(params.get("SECURITY_MAX_RECORDS_PER_HOUR", 500)), "dual_control": str(params.get("SECURITY_DUAL_CONTROL_ENABLED", "true")).lower() == "true" } async def log_event(self, db: AsyncSession, user_id: Optional[int], action: str, severity: LogSeverity, **kwargs): """ LOGIKA MEGŐRIZVE: Audit naplózás + Emergency Lock trigger. """ new_log = AuditLog( user_id=user_id, severity=severity, action=action, target_type=kwargs.get("target_type"), target_id=kwargs.get("target_id"), old_data=kwargs.get("old_data"), new_data=kwargs.get("new_data"), ip_address=kwargs.get("ip"), user_agent=kwargs.get("ua") ) db.add(new_log) if severity == LogSeverity.emergency: await self._execute_emergency_lock(db, user_id, f"Auto-lock by: {action}") await db.commit() async def request_action(self, db: AsyncSession, requester_id: int, action_type: str, payload: Dict, reason: str): """ NÉGY SZEM ELV: Jóváhagyási kérelem indítása. """ new_action = PendingAction( requester_id=requester_id, action_type=action_type, payload=payload, reason=reason, status=ActionStatus.pending ) db.add(new_action) await db.commit() return new_action async def approve_action(self, db: AsyncSession, approver_id: int, action_id: int): """ Jóváhagyás végrehajtása (Logic Preserved: Ön-jóváhagyás tiltva). """ stmt = select(PendingAction).where(PendingAction.id == action_id) action = (await db.execute(stmt)).scalar_one_or_none() if not action or action.status != ActionStatus.pending: raise Exception("Művelet nem található.") if action.requester_id == approver_id: raise Exception("Saját kérést nem hagyhatsz jóvá!") # Üzleti logika (pl. Role változtatás) if action.action_type == "CHANGE_ROLE": target_user = (await db.execute(select(User).where(User.id == action.payload.get("user_id")))).scalar_one_or_none() if target_user: target_user.role = action.payload.get("new_role") action.status = ActionStatus.approved action.approver_id = approver_id action.processed_at = datetime.now(timezone.utc) await db.commit() async def check_data_access_limit(self, db: AsyncSession, user_id: int): """ DATA THROTTLING: Adatlopás elleni védelem. """ config = await self.get_sec_config(db) limit_time = datetime.now(timezone.utc) - timedelta(hours=1) stmt = select(func.count(AuditLog.id)).where( and_(AuditLog.user_id == user_id, AuditLog.timestamp >= limit_time, AuditLog.action.like("GET_%")) ) count = (await db.execute(stmt)).scalar() or 0 if count > config["max_records"]: await self.log_event(db, user_id, "MASS_DATA_ACCESS", LogSeverity.emergency, reason=f"Count: {count}") return False return True async def _execute_emergency_lock(self, db: AsyncSession, user_id: int, reason: str): if not user_id: return user = (await db.execute(select(User).where(User.id == user_id))).scalar_one_or_none() if user: user.is_active = False logger.critical(f"🚨 EMERGENCY LOCK: User {user_id} suspended. Reason: {reason}") security_service = SecurityService()