feat(audit): add audit logs for S3/WebDAV mapping APIs

This commit is contained in:
shiyu
2025-12-29 16:41:02 +08:00
parent 1d5824d498
commit 184997deed
2 changed files with 32 additions and 3 deletions
+6
View File
@@ -8,6 +8,7 @@ from typing import Dict, Iterable, List, Optional, Tuple
from fastapi import APIRouter, Request, Response from fastapi import APIRouter, Request, Response
from fastapi import HTTPException from fastapi import HTTPException
from domain.audit import AuditAction, audit
from domain.config.service import ConfigService from domain.config.service import ConfigService
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs.service import VirtualFSService
@@ -314,6 +315,7 @@ def _resource_path(bucket: str, key: Optional[str] = None) -> str:
@router.get("") @router.get("")
@audit(action=AuditAction.READ, description="S3: 列出桶")
async def list_buckets(request: Request): async def list_buckets(request: Request):
if (resp := await _ensure_enabled()) is not None: if (resp := await _ensure_enabled()) is not None:
return resp return resp
@@ -336,6 +338,7 @@ async def list_buckets(request: Request):
@router.get("/{bucket}") @router.get("/{bucket}")
@audit(action=AuditAction.READ, description="S3: 列出对象")
async def list_objects(request: Request, bucket: str): async def list_objects(request: Request, bucket: str):
if (resp := await _ensure_enabled()) is not None: if (resp := await _ensure_enabled()) is not None:
return resp return resp
@@ -476,6 +479,7 @@ async def _stat_object(settings: S3Settings, key: str) -> Tuple[Optional[Dict],
@router.api_route("/{bucket}/{object_path:path}", methods=["GET", "HEAD"]) @router.api_route("/{bucket}/{object_path:path}", methods=["GET", "HEAD"])
@audit(action=AuditAction.DOWNLOAD, description="S3: 获取对象")
async def object_get_head(request: Request, bucket: str, object_path: str): async def object_get_head(request: Request, bucket: str, object_path: str):
settings, error = await _ensure_bucket_and_auth(request, bucket) settings, error = await _ensure_bucket_and_auth(request, bucket)
if error: if error:
@@ -500,6 +504,7 @@ async def object_get_head(request: Request, bucket: str, object_path: str):
@router.put("/{bucket}/{object_path:path}") @router.put("/{bucket}/{object_path:path}")
@audit(action=AuditAction.UPLOAD, description="S3: 上传对象")
async def put_object(request: Request, bucket: str, object_path: str): async def put_object(request: Request, bucket: str, object_path: str):
settings, error = await _ensure_bucket_and_auth(request, bucket) settings, error = await _ensure_bucket_and_auth(request, bucket)
if error: if error:
@@ -520,6 +525,7 @@ async def put_object(request: Request, bucket: str, object_path: str):
@router.delete("/{bucket}/{object_path:path}") @router.delete("/{bucket}/{object_path:path}")
@audit(action=AuditAction.DELETE, description="S3: 删除对象")
async def delete_object(request: Request, bucket: str, object_path: str): async def delete_object(request: Request, bucket: str, object_path: str):
settings, error = await _ensure_bucket_and_auth(request, bucket) settings, error = await _ensure_bucket_and_auth(request, bucket)
if error: if error:
+26 -3
View File
@@ -8,6 +8,7 @@ from typing import Optional
from fastapi import APIRouter, Request, Response, HTTPException, Depends from fastapi import APIRouter, Request, Response, HTTPException, Depends
import xml.etree.ElementTree as ET import xml.etree.ElementTree as ET
from domain.audit import AuditAction, audit
from domain.auth.service import AuthService from domain.auth.service import AuthService
from domain.auth.types import User, UserInDB from domain.auth.types import User, UserInDB
from domain.virtual_fs.service import VirtualFSService from domain.virtual_fs.service import VirtualFSService
@@ -141,11 +142,13 @@ def _normalize_fs_path(path: str) -> str:
@router.options("/{path:path}") @router.options("/{path:path}")
async def options_root(path: str = "", _enabled: None = Depends(_ensure_webdav_enabled)): @audit(action=AuditAction.READ, description="WebDAV: OPTIONS", user_kw="user")
async def options_root(_request: Request, path: str = "", _enabled: None = Depends(_ensure_webdav_enabled)):
return Response(status_code=200, headers=_dav_headers()) return Response(status_code=200, headers=_dav_headers())
@router.api_route("/{path:path}", methods=["PROPFIND"]) @router.api_route("/{path:path}", methods=["PROPFIND"])
@audit(action=AuditAction.READ, description="WebDAV: PROPFIND", user_kw="user")
async def propfind( async def propfind(
request: Request, request: Request,
path: str, path: str,
@@ -193,6 +196,7 @@ async def propfind(
@router.get("/{path:path}") @router.get("/{path:path}")
@audit(action=AuditAction.DOWNLOAD, description="WebDAV: GET", user_kw="user")
async def dav_get( async def dav_get(
path: str, path: str,
request: Request, request: Request,
@@ -205,8 +209,10 @@ async def dav_get(
@router.head("/{path:path}") @router.head("/{path:path}")
@audit(action=AuditAction.READ, description="WebDAV: HEAD", user_kw="user")
async def dav_head( async def dav_head(
path: str, path: str,
_request: Request,
_enabled: None = Depends(_ensure_webdav_enabled), _enabled: None = Depends(_ensure_webdav_enabled),
user: User = Depends(_get_basic_user), user: User = Depends(_get_basic_user),
): ):
@@ -231,6 +237,7 @@ async def dav_head(
@router.api_route("/{path:path}", methods=["PUT"]) @router.api_route("/{path:path}", methods=["PUT"])
@audit(action=AuditAction.UPLOAD, description="WebDAV: PUT", user_kw="user")
async def dav_put( async def dav_put(
path: str, path: str,
request: Request, request: Request,
@@ -247,8 +254,10 @@ async def dav_put(
@router.api_route("/{path:path}", methods=["DELETE"]) @router.api_route("/{path:path}", methods=["DELETE"])
@audit(action=AuditAction.DELETE, description="WebDAV: DELETE", user_kw="user")
async def dav_delete( async def dav_delete(
path: str, path: str,
_request: Request,
_enabled: None = Depends(_ensure_webdav_enabled), _enabled: None = Depends(_ensure_webdav_enabled),
user: User = Depends(_get_basic_user), user: User = Depends(_get_basic_user),
): ):
@@ -258,8 +267,10 @@ async def dav_delete(
@router.api_route("/{path:path}", methods=["MKCOL"]) @router.api_route("/{path:path}", methods=["MKCOL"])
@audit(action=AuditAction.CREATE, description="WebDAV: MKCOL", user_kw="user")
async def dav_mkcol( async def dav_mkcol(
path: str, path: str,
_request: Request,
_enabled: None = Depends(_ensure_webdav_enabled), _enabled: None = Depends(_ensure_webdav_enabled),
user: User = Depends(_get_basic_user), user: User = Depends(_get_basic_user),
): ):
@@ -281,7 +292,13 @@ def _parse_destination(dest: str) -> str:
@router.api_route("/{path:path}", methods=["MOVE"]) @router.api_route("/{path:path}", methods=["MOVE"])
async def dav_move(path: str, request: Request, user: User = Depends(_get_basic_user)): @audit(action=AuditAction.UPDATE, description="WebDAV: MOVE", user_kw="user")
async def dav_move(
path: str,
request: Request,
_enabled: None = Depends(_ensure_webdav_enabled),
user: User = Depends(_get_basic_user),
):
full_src = _normalize_fs_path(path) full_src = _normalize_fs_path(path)
dest_header = request.headers.get("Destination") dest_header = request.headers.get("Destination")
dst = _parse_destination(dest_header or "") dst = _parse_destination(dest_header or "")
@@ -291,7 +308,13 @@ async def dav_move(path: str, request: Request, user: User = Depends(_get_basic_
@router.api_route("/{path:path}", methods=["COPY"]) @router.api_route("/{path:path}", methods=["COPY"])
async def dav_copy(path: str, request: Request, user: User = Depends(_get_basic_user)): @audit(action=AuditAction.CREATE, description="WebDAV: COPY", user_kw="user")
async def dav_copy(
path: str,
request: Request,
_enabled: None = Depends(_ensure_webdav_enabled),
user: User = Depends(_get_basic_user),
):
full_src = _normalize_fs_path(path) full_src = _normalize_fs_path(path)
dest_header = request.headers.get("Destination") dest_header = request.headers.get("Destination")
dst = _parse_destination(dest_header or "") dst = _parse_destination(dest_header or "")