forked from claw/flaskpaste
feat: integrate unused observability features
- Add request duration metrics via before/after request hooks - Add PKI audit logging: CERT_ISSUED, CERT_REVOKED, AUTH_FAILURE - Wire up observe_request_duration() from metrics.py - Log certificate operations (registration, CA gen, issue, revoke) - Log auth failures for revoked/expired certificates
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import logging
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import uuid
|
||||
|
||||
from flask import Flask, Response, g, request
|
||||
@@ -92,6 +93,30 @@ def setup_request_id(app: Flask) -> None:
|
||||
return response
|
||||
|
||||
|
||||
def setup_request_metrics(app: Flask) -> None:
|
||||
"""Record request duration metrics for Prometheus."""
|
||||
from app.metrics import observe_request_duration
|
||||
|
||||
@app.before_request
|
||||
def record_request_start() -> None:
|
||||
"""Record request start time for duration metrics."""
|
||||
g.request_start_time = time.time()
|
||||
|
||||
@app.after_request
|
||||
def record_request_duration(response: Response) -> Response:
|
||||
"""Record request duration to Prometheus histogram."""
|
||||
start_time = getattr(g, "request_start_time", None)
|
||||
if start_time is not None:
|
||||
duration = time.time() - start_time
|
||||
observe_request_duration(
|
||||
method=request.method,
|
||||
endpoint=request.path,
|
||||
status=response.status_code,
|
||||
duration=duration,
|
||||
)
|
||||
return response
|
||||
|
||||
|
||||
def setup_error_handlers(app: Flask) -> None:
|
||||
"""Register global error handlers with JSON responses."""
|
||||
import json
|
||||
@@ -226,6 +251,10 @@ def create_app(config_name: str | None = None) -> Flask:
|
||||
# Setup metrics (skip in testing)
|
||||
setup_metrics(app)
|
||||
|
||||
# Setup request duration metrics (skip in testing)
|
||||
if not app.config.get("TESTING"):
|
||||
setup_request_metrics(app)
|
||||
|
||||
# Initialize database
|
||||
from app import database
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from flask import Response, current_app, g, request
|
||||
from flask.views import MethodView
|
||||
|
||||
from app.api import bp
|
||||
from app.audit import AuditEvent, AuditOutcome, log_event
|
||||
from app.config import VERSION
|
||||
from app.database import check_content_hash, get_db, hash_password, verify_password
|
||||
from app.metrics import (
|
||||
@@ -445,6 +446,13 @@ def get_client_id() -> str | None:
|
||||
current_app.logger.warning(
|
||||
"Elevated auth rejected (revoked/expired): %s", fingerprint[:12] + "..."
|
||||
)
|
||||
log_event(
|
||||
AuditEvent.AUTH_FAILURE,
|
||||
AuditOutcome.BLOCKED,
|
||||
client_id=fingerprint,
|
||||
client_ip=get_client_ip(),
|
||||
details={"reason": "revoked_or_expired"},
|
||||
)
|
||||
return None
|
||||
return fingerprint
|
||||
|
||||
@@ -1205,6 +1213,17 @@ class RegisterView(MethodView):
|
||||
cert_info["fingerprint_sha1"][:12],
|
||||
request.remote_addr,
|
||||
)
|
||||
log_event(
|
||||
AuditEvent.CERT_ISSUED,
|
||||
AuditOutcome.SUCCESS,
|
||||
client_id=cert_info["fingerprint_sha1"],
|
||||
client_ip=request.remote_addr,
|
||||
details={
|
||||
"type": "registration",
|
||||
"common_name": common_name,
|
||||
"expires_at": cert_info["expires_at"],
|
||||
},
|
||||
)
|
||||
|
||||
# Return PKCS#12 as binary download
|
||||
response = Response(p12_data, mimetype="application/x-pkcs12")
|
||||
@@ -1745,6 +1764,18 @@ class PKICAGenerateView(MethodView):
|
||||
current_app.logger.info(
|
||||
"CA generated: cn=%s fingerprint=%s", common_name, ca_info["fingerprint_sha1"][:12]
|
||||
)
|
||||
log_event(
|
||||
AuditEvent.CERT_ISSUED,
|
||||
AuditOutcome.SUCCESS,
|
||||
client_id=owner,
|
||||
client_ip=request.remote_addr,
|
||||
details={
|
||||
"type": "ca",
|
||||
"fingerprint": ca_info["fingerprint_sha1"][:16],
|
||||
"common_name": common_name,
|
||||
"expires_at": ca_info["expires_at"],
|
||||
},
|
||||
)
|
||||
|
||||
return json_response(
|
||||
{
|
||||
@@ -1829,6 +1860,19 @@ class PKIIssueView(MethodView):
|
||||
cert_info["fingerprint_sha1"][:12],
|
||||
issued_to or "anonymous",
|
||||
)
|
||||
log_event(
|
||||
AuditEvent.CERT_ISSUED,
|
||||
AuditOutcome.SUCCESS,
|
||||
client_id=cert_info["fingerprint_sha1"],
|
||||
client_ip=request.remote_addr,
|
||||
details={
|
||||
"type": "client",
|
||||
"serial": cert_info["serial"][:16],
|
||||
"common_name": common_name,
|
||||
"issued_by": issued_to,
|
||||
"expires_at": cert_info["expires_at"],
|
||||
},
|
||||
)
|
||||
|
||||
# Return certificate bundle
|
||||
return json_response(
|
||||
@@ -1936,6 +1980,16 @@ class PKIRevokeView(MethodView):
|
||||
return error_response("Revocation failed", 500)
|
||||
|
||||
current_app.logger.info("Certificate revoked: serial=%s by=%s", serial[:8], client_id[:12])
|
||||
log_event(
|
||||
AuditEvent.CERT_REVOKED,
|
||||
AuditOutcome.SUCCESS,
|
||||
client_id=client_id,
|
||||
client_ip=get_client_ip(),
|
||||
details={
|
||||
"serial": serial[:16],
|
||||
"fingerprint": row["fingerprint_sha1"][:16],
|
||||
},
|
||||
)
|
||||
|
||||
return json_response({"message": "Certificate revoked", "serial": serial})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user