mirror of
https://git.mirrors.martin98.com/https://github.com/open-webui/open-webui
synced 2025-08-18 04:25:52 +08:00
enh: failed login attempts audit log
This commit is contained in:
parent
43efff0fe6
commit
f2314596ba
@ -37,7 +37,7 @@ if TYPE_CHECKING:
|
|||||||
class AuditLogEntry:
|
class AuditLogEntry:
|
||||||
# `Metadata` audit level properties
|
# `Metadata` audit level properties
|
||||||
id: str
|
id: str
|
||||||
user: dict[str, Any]
|
user: Optional[dict[str, Any]]
|
||||||
audit_level: str
|
audit_level: str
|
||||||
verb: str
|
verb: str
|
||||||
request_uri: str
|
request_uri: str
|
||||||
@ -190,21 +190,40 @@ class AuditLoggingMiddleware:
|
|||||||
finally:
|
finally:
|
||||||
await self._log_audit_entry(request, context)
|
await self._log_audit_entry(request, context)
|
||||||
|
|
||||||
async def _get_authenticated_user(self, request: Request) -> UserModel:
|
async def _get_authenticated_user(self, request: Request) -> Optional[UserModel]:
|
||||||
|
|
||||||
auth_header = request.headers.get("Authorization")
|
auth_header = request.headers.get("Authorization")
|
||||||
assert auth_header
|
|
||||||
user = get_current_user(request, None, get_http_authorization_cred(auth_header))
|
|
||||||
|
|
||||||
return user
|
try:
|
||||||
|
user = get_current_user(
|
||||||
|
request, None, get_http_authorization_cred(auth_header)
|
||||||
|
)
|
||||||
|
return user
|
||||||
|
except Exception as e:
|
||||||
|
logger.debug(f"Failed to get authenticated user: {str(e)}")
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _should_skip_auditing(self, request: Request) -> bool:
|
def _should_skip_auditing(self, request: Request) -> bool:
|
||||||
if (
|
if (
|
||||||
request.method not in {"POST", "PUT", "PATCH", "DELETE"}
|
request.method not in {"POST", "PUT", "PATCH", "DELETE"}
|
||||||
or AUDIT_LOG_LEVEL == "NONE"
|
or AUDIT_LOG_LEVEL == "NONE"
|
||||||
or not request.headers.get("authorization")
|
|
||||||
):
|
):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
ALWAYS_LOG_ENDPOINTS = {
|
||||||
|
"/api/v1/auths/signin",
|
||||||
|
"/api/v1/auths/signout",
|
||||||
|
"/api/v1/auths/signup",
|
||||||
|
}
|
||||||
|
path = request.url.path.lower()
|
||||||
|
for endpoint in ALWAYS_LOG_ENDPOINTS:
|
||||||
|
if path.startswith(endpoint):
|
||||||
|
return False # Do NOT skip logging for auth endpoints
|
||||||
|
|
||||||
|
# Skip logging if the request is not authenticated
|
||||||
|
if not request.headers.get("authorization"):
|
||||||
|
return True
|
||||||
|
|
||||||
# match either /api/<resource>/...(for the endpoint /api/chat case) or /api/v1/<resource>/...
|
# match either /api/<resource>/...(for the endpoint /api/chat case) or /api/v1/<resource>/...
|
||||||
pattern = re.compile(
|
pattern = re.compile(
|
||||||
r"^/api(?:/v1)?/(" + "|".join(self.excluded_paths) + r")\b"
|
r"^/api(?:/v1)?/(" + "|".join(self.excluded_paths) + r")\b"
|
||||||
@ -231,17 +250,32 @@ class AuditLoggingMiddleware:
|
|||||||
try:
|
try:
|
||||||
user = await self._get_authenticated_user(request)
|
user = await self._get_authenticated_user(request)
|
||||||
|
|
||||||
|
user = (
|
||||||
|
user.model_dump(include={"id", "name", "email", "role"}) if user else {}
|
||||||
|
)
|
||||||
|
|
||||||
|
request_body = context.request_body.decode("utf-8", errors="replace")
|
||||||
|
response_body = context.response_body.decode("utf-8", errors="replace")
|
||||||
|
|
||||||
|
# Redact sensitive information
|
||||||
|
if "password" in request_body:
|
||||||
|
request_body = re.sub(
|
||||||
|
r'"password":\s*"(.*?)"',
|
||||||
|
'"password": "********"',
|
||||||
|
request_body,
|
||||||
|
)
|
||||||
|
|
||||||
entry = AuditLogEntry(
|
entry = AuditLogEntry(
|
||||||
id=str(uuid.uuid4()),
|
id=str(uuid.uuid4()),
|
||||||
user=user.model_dump(include={"id", "name", "email", "role"}),
|
user=user,
|
||||||
audit_level=self.audit_level.value,
|
audit_level=self.audit_level.value,
|
||||||
verb=request.method,
|
verb=request.method,
|
||||||
request_uri=str(request.url),
|
request_uri=str(request.url),
|
||||||
response_status_code=context.metadata.get("response_status_code", None),
|
response_status_code=context.metadata.get("response_status_code", None),
|
||||||
source_ip=request.client.host if request.client else None,
|
source_ip=request.client.host if request.client else None,
|
||||||
user_agent=request.headers.get("user-agent"),
|
user_agent=request.headers.get("user-agent"),
|
||||||
request_object=context.request_body.decode("utf-8", errors="replace"),
|
request_object=request_body,
|
||||||
response_object=context.response_body.decode("utf-8", errors="replace"),
|
response_object=response_body,
|
||||||
)
|
)
|
||||||
|
|
||||||
self.audit_logger.write(entry)
|
self.audit_logger.write(entry)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user