jebin2 commited on
Commit
5e3877e
·
1 Parent(s): be85b16
Files changed (3) hide show
  1. core/security.py +0 -32
  2. routers/auth.py +20 -16
  3. services/audit_service.py +82 -0
core/security.py DELETED
@@ -1,32 +0,0 @@
1
- """
2
- Core Security Utilities
3
-
4
- Note: Secret key authentication has been replaced with Google OAuth.
5
- The bcrypt functions below are kept for potential future use (e.g., admin passwords).
6
- """
7
- import bcrypt
8
-
9
-
10
- def verify_password(plain_password: str, hashed_password: str) -> bool:
11
- """
12
- Verify a password against a bcrypt hash.
13
-
14
- Note: This is no longer used for user authentication (moved to Google OAuth).
15
- Kept for potential admin/internal use cases.
16
- """
17
- if isinstance(hashed_password, str):
18
- hashed_password = hashed_password.encode('utf-8')
19
- return bcrypt.checkpw(plain_password.encode('utf-8'), hashed_password)
20
-
21
-
22
- def get_password_hash(password: str) -> str:
23
- """
24
- Hash a password using bcrypt.
25
-
26
- Note: This is no longer used for user authentication (moved to Google OAuth).
27
- Kept for potential admin/internal use cases.
28
- """
29
- salt = bcrypt.gensalt(rounds=12)
30
- hashed = bcrypt.hashpw(password.encode('utf-8'), salt)
31
- return hashed.decode('utf-8')
32
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
routers/auth.py CHANGED
@@ -25,14 +25,19 @@ from core.schemas import (
25
  from services.auth_service.google_provider import (
26
  GoogleAuthService,
27
  GoogleUserInfo,
28
- InvalidTokenError,
 
 
29
  )
30
  from services.auth_service.jwt_provider import (
31
  JWTService,
32
  create_access_token,
 
 
33
  )
34
  from dependencies import check_rate_limit, get_current_user
35
  from services.drive_service import DriveService
 
36
 
37
  logger = logging.getLogger(__name__)
38
 
@@ -105,15 +110,14 @@ async def google_auth(
105
  logger.warning(f"Invalid Google token from {ip}: {e}")
106
 
107
  # Log failed attempt
108
- audit_log = AuditLog(
 
109
  log_type="server",
110
- user_id=None,
111
  action="google_auth",
112
- ip_address=ip,
113
  status="failed",
114
- error_message=str(e)
 
115
  )
116
- db.add(audit_log)
117
  await db.commit()
118
 
119
  raise HTTPException(
@@ -185,15 +189,15 @@ async def google_auth(
185
  db.add(client_user)
186
 
187
  # Log successful auth
188
- audit_log = AuditLog(
 
189
  log_type="server",
190
- user_id=user.id, # Integer FK to users.id
191
  client_user_id=request.temp_user_id,
192
  action="google_auth",
193
- ip_address=ip,
194
- status="success"
195
  )
196
- db.add(audit_log)
197
  await db.commit()
198
 
199
  # Create our JWT access token with current token_version
@@ -327,14 +331,14 @@ async def logout(
327
  logger.info(f"User {user.user_id} logged out. Token version incremented to {user.token_version}")
328
 
329
  # Log logout
330
- audit_log = AuditLog(
 
331
  log_type="server",
332
- user_id=user.id, # Integer FK to users.id
333
  action="logout",
334
- ip_address=ip,
335
- status="success"
336
  )
337
- db.add(audit_log)
338
  await db.commit()
339
 
340
  # Sync DB to Drive (Async)
 
25
  from services.auth_service.google_provider import (
26
  GoogleAuthService,
27
  GoogleUserInfo,
28
+ InvalidTokenError as GoogleInvalidTokenError,
29
+ ConfigurationError as GoogleConfigError,
30
+ get_google_auth_service,
31
  )
32
  from services.auth_service.jwt_provider import (
33
  JWTService,
34
  create_access_token,
35
+ get_jwt_service,
36
+ InvalidTokenError as JWTInvalidTokenError,
37
  )
38
  from dependencies import check_rate_limit, get_current_user
39
  from services.drive_service import DriveService
40
+ from services.audit_service import AuditService
41
 
42
  logger = logging.getLogger(__name__)
43
 
 
110
  logger.warning(f"Invalid Google token from {ip}: {e}")
111
 
112
  # Log failed attempt
113
+ await AuditService.log_event(
114
+ db=db,
115
  log_type="server",
 
116
  action="google_auth",
 
117
  status="failed",
118
+ error_message=str(e),
119
+ request=req
120
  )
 
121
  await db.commit()
122
 
123
  raise HTTPException(
 
189
  db.add(client_user)
190
 
191
  # Log successful auth
192
+ await AuditService.log_event(
193
+ db=db,
194
  log_type="server",
195
+ user_id=user.id,
196
  client_user_id=request.temp_user_id,
197
  action="google_auth",
198
+ status="success",
199
+ request=req
200
  )
 
201
  await db.commit()
202
 
203
  # Create our JWT access token with current token_version
 
331
  logger.info(f"User {user.user_id} logged out. Token version incremented to {user.token_version}")
332
 
333
  # Log logout
334
+ await AuditService.log_event(
335
+ db=db,
336
  log_type="server",
337
+ user_id=user.id,
338
  action="logout",
339
+ status="success",
340
+ request=req
341
  )
 
342
  await db.commit()
343
 
344
  # Sync DB to Drive (Async)
services/audit_service.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Audit Service - Handle security and activity logging.
3
+ """
4
+ import logging
5
+ from datetime import datetime
6
+ from typing import Optional, Dict, Any
7
+ from fastapi import Request
8
+ from sqlalchemy.ext.asyncio import AsyncSession
9
+ from core.models import AuditLog
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+
14
+ class AuditService:
15
+ """
16
+ Service for creating audit logs.
17
+ """
18
+
19
+ @staticmethod
20
+ async def log_event(
21
+ db: AsyncSession,
22
+ action: str,
23
+ status: str,
24
+ user_id: Optional[int] = None,
25
+ client_user_id: Optional[str] = None,
26
+ details: Optional[Dict[str, Any]] = None,
27
+ request: Optional[Request] = None,
28
+ error_message: Optional[str] = None,
29
+ log_type: str = "client"
30
+ ) -> AuditLog:
31
+ """
32
+ Create an audit log entry.
33
+
34
+ Args:
35
+ db: Database session
36
+ action: Event action (e.g., "login", "logout")
37
+ status: Outcome (e.g., "success", "failure")
38
+ user_id: ID of the authenticated user (if any)
39
+ client_user_id: Client-side ID (if any)
40
+ details: Additional metadata
41
+ request: FastAPI request object (for IP/UA)
42
+ error_message: Error description if failed
43
+ log_type: "client" or "server"
44
+
45
+ Returns:
46
+ The created AuditLog instance
47
+ """
48
+ ip_address = None
49
+ user_agent = None
50
+ refer_url = None
51
+
52
+ if request:
53
+ ip_address = request.client.host if request.client else None
54
+ user_agent = request.headers.get("user-agent")
55
+ refer_url = request.headers.get("referer")
56
+
57
+ # Create log entry
58
+ audit_log = AuditLog(
59
+ log_type=log_type,
60
+ user_id=user_id,
61
+ client_user_id=client_user_id,
62
+ action=action,
63
+ details=details or {},
64
+ ip_address=ip_address,
65
+ user_agent=user_agent,
66
+ refer_url=refer_url,
67
+ status=status,
68
+ error_message=error_message,
69
+ timestamp=datetime.utcnow()
70
+ )
71
+
72
+ db.add(audit_log)
73
+ # We don't commit here to allow the caller to group with other transactions
74
+ # or commit explicitly.
75
+
76
+ logger.info(f"Audit Log: [{status}] {action} - User: {user_id or 'Anon'} - IP: {ip_address}")
77
+
78
+ return audit_log
79
+
80
+ # Global instance not strictly needed as it's a static method helper,
81
+ # but keeping pattern consistent if we add state later.
82
+ audit_service = AuditService()