Files
Aegis/backend/app/services/audit_query_service.py
T
kitos 4dea19cae9
Aegis CI / lint-and-test (push) Has been cancelled
Snyk Security Scan / Python vulnerabilities (backend) (push) Has been cancelled
Snyk Security Scan / npm vulnerabilities (frontend) (push) Has been cancelled
Snyk Security Scan / Docker image vulnerabilities (backend) (push) Has been cancelled
feat(users): passwordless user creation via emailed set-password link, display full name instead of username
Admins now create users with a name + email (no password) and role;
a Send Email action (per-user, also reusable for resets) issues a
one-time token and POSTs it to an admin-configurable webhook URL —
intended for a Power Automate flow that delivers the actual email.
The old admin-sets-the-password flow is kept server-side
(LegacyUserCreateWithPassword) but no longer wired into the UI.

Every user-facing surface (top bar, sidebar, user management, audit
log, assignee pickers, dispute notifications) now shows full_name
instead of username, falling back to username when unset.

Also fixes long attack_procedure/expected_detection/suggested_text
text overflowing its container in the template review panels and
Red/Blue team fields (missing break-words on whitespace-pre-wrap
blocks).
2026-07-17 16:58:25 +02:00

148 lines
4.4 KiB
Python

"""Audit log query service — framework-agnostic query logic for audit logs.
Provides paginated logs and distinct action/entity-type lists.
No FastAPI imports.
"""
# Enable future language features for compatibility
from __future__ import annotations
# Import datetime from datetime
from datetime import datetime
# Import Session, joinedload from sqlalchemy.orm
from sqlalchemy.orm import Session, joinedload
# Import AuditLog from app.models.audit
from app.models.audit import AuditLog
# Define function list_logs
def list_logs(
# Entry: db
db: Session,
*,
# Entry: user_id
user_id: str | None = None,
# Entry: action
action: str | None = None,
# Entry: entity_type
entity_type: str | None = None,
# Entry: start_date
start_date: datetime | None = None,
# Entry: end_date
end_date: datetime | None = None,
# Entry: offset
offset: int = 0,
# Entry: limit
limit: int = 50,
) -> dict:
"""Return paginated audit logs with optional filters.
Returns a dict with keys: items, total, offset, limit.
Each item is a dict with: id, user_id, username, action, entity_type,
entity_id, timestamp, details.
"""
# Assign query = db.query(AuditLog).options(joinedload(AuditLog.user))
query = db.query(AuditLog).options(joinedload(AuditLog.user))
# Check: user_id
if user_id:
# Assign query = query.filter(AuditLog.user_id == user_id)
query = query.filter(AuditLog.user_id == user_id)
# Check: action
if action:
# Assign query = query.filter(AuditLog.action == action)
query = query.filter(AuditLog.action == action)
# Check: entity_type
if entity_type:
# Assign query = query.filter(AuditLog.entity_type == entity_type)
query = query.filter(AuditLog.entity_type == entity_type)
# Check: start_date
if start_date:
# Assign query = query.filter(AuditLog.timestamp >= start_date)
query = query.filter(AuditLog.timestamp >= start_date)
# Check: end_date
if end_date:
# Assign query = query.filter(AuditLog.timestamp <= end_date)
query = query.filter(AuditLog.timestamp <= end_date)
# Assign total = query.count()
total = query.count()
# Assign logs = (
logs = (
query
# Chain .order_by() call
.order_by(AuditLog.timestamp.desc())
# Chain .offset() call
.offset(offset)
# Chain .limit() call
.limit(limit)
# Chain .all() call
.all()
)
# Assign items = [
items = [
{
# Literal argument value
"id": log.id,
# Literal argument value
"user_id": log.user_id,
# Literal argument value
"username": log.user.username if log.user else None,
"full_name": log.user.full_name if log.user else None,
# Literal argument value
"action": log.action,
# Literal argument value
"entity_type": log.entity_type,
# Literal argument value
"entity_id": log.entity_id,
# Literal argument value
"timestamp": log.timestamp,
# Literal argument value
"details": log.details,
}
for log in logs
]
# Return {"items": items, "total": total, "offset": offset, "limit": limit}
return {"items": items, "total": total, "offset": offset, "limit": limit}
# Define function list_distinct_actions
def list_distinct_actions(db: Session) -> list[str]:
"""Return a list of distinct action types in the audit log."""
# Assign actions = (
actions = (
db.query(AuditLog.action)
# Chain .distinct() call
.distinct()
# Chain .order_by() call
.order_by(AuditLog.action)
# Chain .all() call
.all()
)
# Return [a[0] for a in actions]
return [a[0] for a in actions]
# Define function list_distinct_entity_types
def list_distinct_entity_types(db: Session) -> list[str]:
"""Return a list of distinct entity types in the audit log."""
# Assign types = (
types = (
db.query(AuditLog.entity_type)
# Chain .filter() call
.filter(AuditLog.entity_type.isnot(None))
# Chain .distinct() call
.distinct()
# Chain .order_by() call
.order_by(AuditLog.entity_type)
# Chain .all() call
.all()
)
# Return [t[0] for t in types]
return [t[0] for t in types]