mirror of
https://github.com/complexcaresolutions/dak.c2s.git
synced 2026-03-18 03:43:41 +00:00
feat: add audit logging for login, logout, imports, and report generation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4a3648a018
commit
9650889d24
3 changed files with 59 additions and 3 deletions
|
|
@ -1,10 +1,11 @@
|
||||||
"""Auth API router: login, register, refresh, logout, MFA, password change."""
|
"""Auth API router: login, register, refresh, logout, MFA, password change."""
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends
|
from fastapi import APIRouter, Depends, Request
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
from app.core.dependencies import get_current_user
|
from app.core.dependencies import get_current_user
|
||||||
from app.database import get_db
|
from app.database import get_db
|
||||||
|
from app.services.audit_service import log_action
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.schemas.auth import (
|
from app.schemas.auth import (
|
||||||
ChangePasswordRequest,
|
ChangePasswordRequest,
|
||||||
|
|
@ -36,10 +37,19 @@ router = APIRouter()
|
||||||
|
|
||||||
|
|
||||||
@router.post("/login", response_model=TokenResponse)
|
@router.post("/login", response_model=TokenResponse)
|
||||||
def login(data: LoginRequest, db: Session = Depends(get_db)):
|
def login(data: LoginRequest, request: Request, db: Session = Depends(get_db)):
|
||||||
"""Authenticate with email/password (+ optional MFA) and receive tokens."""
|
"""Authenticate with email/password (+ optional MFA) and receive tokens."""
|
||||||
user = authenticate_user(db, data.email, data.password, data.mfa_code)
|
user = authenticate_user(db, data.email, data.password, data.mfa_code)
|
||||||
access, refresh = create_tokens(db, user)
|
access, refresh = create_tokens(db, user)
|
||||||
|
log_action(
|
||||||
|
db,
|
||||||
|
user_id=user.id,
|
||||||
|
action="login",
|
||||||
|
entity_type="user",
|
||||||
|
entity_id=user.id,
|
||||||
|
ip_address=request.client.host if request.client else None,
|
||||||
|
user_agent=request.headers.get("user-agent"),
|
||||||
|
)
|
||||||
return TokenResponse(
|
return TokenResponse(
|
||||||
access_token=access,
|
access_token=access,
|
||||||
refresh_token=refresh,
|
refresh_token=refresh,
|
||||||
|
|
@ -79,11 +89,21 @@ def refresh(data: RefreshRequest, db: Session = Depends(get_db)):
|
||||||
@router.post("/logout")
|
@router.post("/logout")
|
||||||
def logout(
|
def logout(
|
||||||
data: RefreshRequest,
|
data: RefreshRequest,
|
||||||
|
request: Request,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
current_user: User = Depends(get_current_user),
|
current_user: User = Depends(get_current_user),
|
||||||
):
|
):
|
||||||
"""Revoke the supplied refresh token (logout)."""
|
"""Revoke the supplied refresh token (logout)."""
|
||||||
revoke_refresh_token(db, data.refresh_token)
|
revoke_refresh_token(db, data.refresh_token)
|
||||||
|
log_action(
|
||||||
|
db,
|
||||||
|
user_id=current_user.id,
|
||||||
|
action="logout",
|
||||||
|
entity_type="user",
|
||||||
|
entity_id=current_user.id,
|
||||||
|
ip_address=request.client.host if request.client else None,
|
||||||
|
user_agent=request.headers.get("user-agent"),
|
||||||
|
)
|
||||||
return {"detail": "Logged out"}
|
return {"detail": "Logged out"}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ from app.database import get_db
|
||||||
from app.models.audit import ImportLog
|
from app.models.audit import ImportLog
|
||||||
from app.models.user import User
|
from app.models.user import User
|
||||||
from app.schemas.import_schemas import ImportPreview, ImportResult
|
from app.schemas.import_schemas import ImportPreview, ImportResult
|
||||||
|
from app.services.audit_service import log_action
|
||||||
from app.services.csv_parser import parse_csv
|
from app.services.csv_parser import parse_csv
|
||||||
from app.services.icd_service import import_icd_from_xlsx
|
from app.services.icd_service import import_icd_from_xlsx
|
||||||
from app.services.import_service import confirm_import, preview_import
|
from app.services.import_service import confirm_import, preview_import
|
||||||
|
|
@ -140,7 +141,20 @@ async def confirm_csv_import(
|
||||||
detail=f"CSV parsing failed: {exc}",
|
detail=f"CSV parsing failed: {exc}",
|
||||||
)
|
)
|
||||||
|
|
||||||
return confirm_import(db, parsed, file.filename, user.id)
|
result = confirm_import(db, parsed, file.filename, user.id)
|
||||||
|
log_action(
|
||||||
|
db,
|
||||||
|
user_id=user.id,
|
||||||
|
action="csv_imported",
|
||||||
|
entity_type="import",
|
||||||
|
new_values={
|
||||||
|
"filename": file.filename,
|
||||||
|
"imported": result.imported,
|
||||||
|
"skipped": result.skipped,
|
||||||
|
"updated": result.updated,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
@ -194,6 +208,18 @@ async def upload_icd_xlsx(
|
||||||
db.add(log)
|
db.add(log)
|
||||||
db.commit()
|
db.commit()
|
||||||
|
|
||||||
|
log_action(
|
||||||
|
db,
|
||||||
|
user_id=user.id,
|
||||||
|
action="icd_xlsx_imported",
|
||||||
|
entity_type="import",
|
||||||
|
new_values={
|
||||||
|
"filename": file.filename,
|
||||||
|
"updated": result["updated"],
|
||||||
|
"errors": len(result["errors"]),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
return ICDImportResponse(**result)
|
return ICDImportResponse(**result)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ from app.schemas.report import (
|
||||||
ReportListResponse,
|
ReportListResponse,
|
||||||
ReportMeta,
|
ReportMeta,
|
||||||
)
|
)
|
||||||
|
from app.services.audit_service import log_action
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
@ -144,6 +145,15 @@ def generate_report(
|
||||||
db.commit()
|
db.commit()
|
||||||
db.refresh(report)
|
db.refresh(report)
|
||||||
|
|
||||||
|
log_action(
|
||||||
|
db,
|
||||||
|
user_id=user.id,
|
||||||
|
action="report_generated",
|
||||||
|
entity_type="report",
|
||||||
|
entity_id=report.id,
|
||||||
|
new_values={"jahr": jahr, "kw": kw, "filename": filename},
|
||||||
|
)
|
||||||
|
|
||||||
return ReportMeta.model_validate(report)
|
return ReportMeta.model_validate(report)
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
||||||
raise HTTPException(501, f"Required service not yet available: {exc}")
|
raise HTTPException(501, f"Required service not yet available: {exc}")
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue