fix(auth): resolve JWT InvalidSignatureError and improve frontend UX
Some checks failed
CI / lint-and-test (push) Has been cancelled

- Fix auth by using idToken fallback when accessToken audience mismatches
- Add PyJWT verification with audience-aware token selection in frontend
- Source health: track last_attempt_time and error status per source
- Frontend: fix modal outside x-data scope, add circular-safe JSON stringify
- Frontend: support multi-select service filter with All/None toggles
- Frontend: improve filter layout into organized rows
- Frontend: fix text overflow and result pill colors (success/succeeded)
- Intune: normalize application actors (auditActorType=Application)
- Add cache-control middleware for HTML/API responses
- Update tests for multi-service filtering and source health
This commit is contained in:
2026-04-16 11:32:45 +02:00
parent ed310a06de
commit 82bafc06c9
12 changed files with 350 additions and 103 deletions

View File

@@ -34,6 +34,7 @@ def _decode_cursor(cursor: str) -> tuple[str, str]:
@router.get("/events", response_model=PaginatedEventResponse)
def list_events(
service: str | None = None,
services: list[str] | None = Query(default=None),
actor: str | None = None,
operation: str | None = None,
result: str | None = None,
@@ -48,6 +49,8 @@ def list_events(
if service:
filters.append({"service": service})
if services:
filters.append({"service": {"$in": services}})
if actor:
actor_safe = re.escape(actor)
filters.append(

View File

@@ -31,12 +31,13 @@ def run_fetch(hours: int = 168):
try:
since = get_watermark(source_key)
result = fn(since=since) if since else fn(hours=window)
set_watermark(source_key, now)
set_watermark(source_key, now, status="healthy")
track_fetch(source_key, len(result))
return result
except Exception as exc:
errors.append(f"{label}: {exc}")
track_fetch_error(source_key)
set_watermark(source_key, now, status="error")
return []
finally:
track_fetch_duration(source_key, time.time() - start_time)

View File

@@ -15,16 +15,21 @@ def source_health():
results = []
for source in SOURCES:
doc = watermarks_collection.find_one({"source": source})
if doc and doc.get("last_fetch_time"):
if doc:
status = doc.get("status")
if not status:
status = "healthy" if doc.get("last_fetch_time") else "unknown"
results.append({
"source": source,
"last_fetch_time": doc["last_fetch_time"],
"status": "healthy",
"last_fetch_time": doc.get("last_fetch_time"),
"last_attempt_time": doc.get("last_attempt_time"),
"status": status,
})
else:
results.append({
"source": source,
"last_fetch_time": None,
"last_attempt_time": None,
"status": "unknown",
})
return results