|
1 | 1 | import logging |
2 | 2 | from datetime import datetime, timedelta |
3 | | -from typing import Dict, Any, Optional |
| 3 | +from typing import Dict, Any, Optional, List |
4 | 4 | from config.config import DatabaseConfig |
| 5 | +from pydantic import BaseModel |
5 | 6 | import json |
6 | 7 | import hashlib |
7 | 8 | import smtplib |
|
11 | 12 | logger = logging.getLogger("mcp.security") |
12 | 13 | logger.setLevel(logging.INFO) |
13 | 14 |
|
| 15 | +class UserActionsInput(BaseModel): |
| 16 | + user_id: str |
| 17 | + page: int = 1 |
| 18 | + page_size: int = 50 |
| 19 | + |
| 20 | +class UserActionsOutput(BaseModel): |
| 21 | + actions: List[Dict[str, Any]] |
| 22 | + total_pages: int |
| 23 | + current_page: int |
| 24 | + |
14 | 25 | class SecurityHandler: |
15 | 26 | def __init__(self, db: DatabaseConfig): |
16 | 27 | self.db = db |
@@ -47,7 +58,7 @@ async def log_event(self, event_type: str, user_id: Optional[str], details: Dict |
47 | 58 | except Exception as e: |
48 | 59 | logger.error(f"Error logging security event: {str(e)}") |
49 | 60 |
|
50 | | - async def log_user_action(self, user_id: str, action: str, details: Dict[str, Any], ip_address: Optional[str] = None): |
| 61 | + async def log_user_action(self, user_id: Optional[str], action: str, details: Dict[str, Any], ip_address: Optional[str] = None): |
51 | 62 | """Log user actions for auditing purposes.""" |
52 | 63 | try: |
53 | 64 | await self.db.query( |
@@ -238,3 +249,36 @@ async def detect_anomalies(self, event_type: str, user_id: Optional[str], ip_add |
238 | 249 | logger.warning(f"Anomaly detected: High cash-out attempts for user {user_id}") |
239 | 250 | except Exception as e: |
240 | 251 | logger.error(f"Error detecting anomalies: {str(e)}") |
| 252 | + |
| 253 | + async def get_user_actions(self, input: UserActionsInput) -> UserActionsOutput: |
| 254 | + try: |
| 255 | + offset = (input.page - 1) * input.page_size |
| 256 | + total_count = await self.db.query( |
| 257 | + "SELECT COUNT(*) FROM audit_logs WHERE user_id = $1", |
| 258 | + [input.user_id] |
| 259 | + ) |
| 260 | + total_pages = (total_count.rows[0]["count"] + input.page_size - 1) // input.page_size |
| 261 | + |
| 262 | + actions = await self.db.query( |
| 263 | + "SELECT action, details, created_at FROM audit_logs WHERE user_id = $1 ORDER BY created_at DESC LIMIT $2 OFFSET $3", |
| 264 | + [input.user_id, input.page_size, offset] |
| 265 | + ) |
| 266 | + await self.log_user_action( |
| 267 | + user_id=input.user_id, |
| 268 | + action="view_action_history", |
| 269 | + details={"page": input.page, "page_size": input.page_size, "action_count": len(actions.rows)} |
| 270 | + ) |
| 271 | + logger.info(f"Retrieved action history for user {input.user_id}, page {input.page}") |
| 272 | + return UserActionsOutput( |
| 273 | + actions=[{"action": row["action"], "details": json.loads(row["details"]), "created_at": row["created_at"].isoformat()} for row in actions.rows], |
| 274 | + total_pages=total_pages, |
| 275 | + current_page=input.page |
| 276 | + ) |
| 277 | + except Exception as e: |
| 278 | + logger.error(f"Error retrieving user actions: {str(e)}") |
| 279 | + await self.log_event( |
| 280 | + event_type="action_history_error", |
| 281 | + user_id=input.user_id, |
| 282 | + details={"error": str(e)} |
| 283 | + ) |
| 284 | + raise HTTPException(status_code=500, detail=str(e)) |
0 commit comments