Repository navigation
Expand file tree
/
Copy pathdjango_middleware.py
More file actions
58 lines (44 loc) · 2.29 KB
/
Copy pathdjango_middleware.py
File metadata and controls
58 lines (44 loc) · 2.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
"""Django middleware example — score every request to high-value endpoints.
Add to settings.py MIDDLEWARE:
"yourapp.middleware.SentinelMiddleware",
Then ensure your frontend forwards the Sentinel token via the
X-Sentinel-Token header, and the device event ID via
X-Sentinel-Fingerprint-Event-Id. Only a live, non-degraded allow decision reaches
the guarded view; test/sample/sandbox results and review require a separate
verification flow. This is a middleware example,
not that verification flow's implementation.
"""
import logging
from django.http import JsonResponse
from sentinel import Sentinel, SentinelError
log = logging.getLogger(__name__)
_GUARDED_PATHS = ("/api/checkout", "/api/withdraw", "/api/transfer")
class SentinelMiddleware:
def __init__(self, get_response):
self.get_response = get_response
self.sentinel = Sentinel() # reads MASKBREAK_API_KEY, older names as fallback
def __call__(self, request):
if not request.path.startswith(_GUARDED_PATHS):
return self.get_response(request)
token = request.META.get("HTTP_X_SENTINEL_TOKEN")
if not token:
return JsonResponse({"error": "missing X-Sentinel-Token"}, status=400)
try:
result = self.sentinel.evaluate(token=token,
fingerprint_event_id=request.META.get("HTTP_X_SENTINEL_FINGERPRINT_EVENT_ID"))
except SentinelError as e:
log.warning("Sentinel error: %s", e)
# Unknown state must not authorize checkout, withdrawal or transfer.
return JsonResponse({"error": "verification unavailable"}, status=503)
# A test or sample answer is not a real verdict: it gets the 409 below.
if result.decision == "block" and not result.is_fixture:
return JsonResponse(
{"error": "blocked", "risk_score": result.risk_score, "reasons": result.reasons},
status=403,
)
if (result.decision != "allow" or result.test or
any(result.raw.get(flag) for flag in ("test", "sample", "sandbox", "degraded"))):
return JsonResponse({"error": "additional verification required"}, status=409)
# Stash on request so the view can read decision/score
request.sentinel = result
return self.get_response(request)