Files
DMARC-Sentinel/app/models.py
T
2026-05-16 12:05:36 -03:00

206 lines
11 KiB
Python

from __future__ import annotations
from datetime import date, datetime, timezone
from sqlalchemy import Boolean, Date, DateTime, Float, ForeignKey, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db import Base
def utcnow() -> datetime:
return datetime.now(timezone.utc)
class InboxStatus(Base):
__tablename__ = "inbox_statuses"
id: Mapped[int] = mapped_column(primary_key=True)
inbox_id: Mapped[str] = mapped_column(String(120), unique=True, index=True)
label: Mapped[str] = mapped_column(String(200))
domain: Mapped[str] = mapped_column(String(255), index=True)
folder: Mapped[str] = mapped_column(String(255))
recipient: Mapped[str] = mapped_column(String(320))
enabled: Mapped[bool] = mapped_column(Boolean, default=True)
last_check_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
last_success_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
last_error_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
last_error: Mapped[str | None] = mapped_column(Text)
last_new_messages: Mapped[int] = mapped_column(Integer, default=0)
last_reports_imported: Mapped[int] = mapped_column(Integer, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
class MailMessage(Base):
__tablename__ = "mail_messages"
__table_args__ = (UniqueConstraint("inbox_id", "folder", "imap_uid", name="uq_message_uid"),)
id: Mapped[int] = mapped_column(primary_key=True)
inbox_id: Mapped[str] = mapped_column(String(120), index=True)
imap_uid: Mapped[str] = mapped_column(String(120))
message_id: Mapped[str | None] = mapped_column(String(500))
folder: Mapped[str] = mapped_column(String(255))
subject: Mapped[str | None] = mapped_column(Text)
sender: Mapped[str | None] = mapped_column(Text)
recipient: Mapped[str | None] = mapped_column(Text)
message_date: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
seen: Mapped[bool] = mapped_column(Boolean, default=False)
status: Mapped[str] = mapped_column(String(40), default="skipped")
error: Mapped[str | None] = mapped_column(Text)
processed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
reports: Mapped[list["Report"]] = relationship(back_populates="mail_message")
class Report(Base):
__tablename__ = "reports"
id: Mapped[int] = mapped_column(primary_key=True)
inbox_id: Mapped[str] = mapped_column(String(120), index=True)
mail_message_id: Mapped[int | None] = mapped_column(ForeignKey("mail_messages.id"))
raw_xml_sha256: Mapped[str] = mapped_column(String(64), unique=True, index=True)
report_id: Mapped[str | None] = mapped_column(String(500), index=True)
org_name: Mapped[str | None] = mapped_column(String(255), index=True)
org_email: Mapped[str | None] = mapped_column(String(320))
extra_contact_info: Mapped[str | None] = mapped_column(Text)
domain: Mapped[str] = mapped_column(String(255), index=True)
date_begin: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
date_end: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), index=True)
policy_p: Mapped[str | None] = mapped_column(String(40))
policy_sp: Mapped[str | None] = mapped_column(String(40))
policy_pct: Mapped[int | None] = mapped_column(Integer)
adkim: Mapped[str | None] = mapped_column(String(20))
aspf: Mapped[str | None] = mapped_column(String(20))
fo: Mapped[str | None] = mapped_column(String(80))
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
mail_message: Mapped[MailMessage | None] = relationship(back_populates="reports")
records: Mapped[list["Record"]] = relationship(back_populates="report", cascade="all, delete-orphan")
class Record(Base):
__tablename__ = "records"
id: Mapped[int] = mapped_column(primary_key=True)
report_id: Mapped[int] = mapped_column(ForeignKey("reports.id"), index=True)
source_ip: Mapped[str] = mapped_column(String(80), index=True)
source_reverse_dns: Mapped[str | None] = mapped_column(String(255))
source_asn: Mapped[str | None] = mapped_column(String(80))
source_country: Mapped[str | None] = mapped_column(String(80))
count: Mapped[int] = mapped_column(Integer, default=0)
disposition: Mapped[str | None] = mapped_column(String(40), index=True)
policy_dkim: Mapped[str | None] = mapped_column(String(40))
policy_spf: Mapped[str | None] = mapped_column(String(40))
dkim_aligned: Mapped[bool] = mapped_column(Boolean, default=False)
spf_aligned: Mapped[bool] = mapped_column(Boolean, default=False)
dmarc_pass: Mapped[bool] = mapped_column(Boolean, default=False)
header_from: Mapped[str | None] = mapped_column(String(255), index=True)
reason_type: Mapped[str | None] = mapped_column(String(120))
reason_comment: Mapped[str | None] = mapped_column(Text)
known_sender_id: Mapped[str | None] = mapped_column(String(120), index=True)
known_sender_name: Mapped[str | None] = mapped_column(String(255))
is_known_sender: Mapped[bool] = mapped_column(Boolean, default=False, index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
report: Mapped[Report] = relationship(back_populates="records")
auth_results: Mapped[list["AuthResult"]] = relationship(back_populates="record", cascade="all, delete-orphan")
class AuthResult(Base):
__tablename__ = "auth_results"
id: Mapped[int] = mapped_column(primary_key=True)
record_id: Mapped[int] = mapped_column(ForeignKey("records.id"), index=True)
auth_type: Mapped[str] = mapped_column(String(20), index=True)
domain: Mapped[str | None] = mapped_column(String(255), index=True)
selector: Mapped[str | None] = mapped_column(String(120))
scope: Mapped[str | None] = mapped_column(String(120))
result: Mapped[str | None] = mapped_column(String(120))
human_result: Mapped[str | None] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
record: Mapped[Record] = relationship(back_populates="auth_results")
class SkippedReportPayload(Base):
__tablename__ = "skipped_report_payloads"
__table_args__ = (
UniqueConstraint("inbox_id", "folder", "imap_uid", "raw_xml_sha256", "reason", name="uq_skipped_report_payload"),
)
id: Mapped[int] = mapped_column(primary_key=True)
inbox_id: Mapped[str] = mapped_column(String(120), index=True)
folder: Mapped[str] = mapped_column(String(255))
imap_uid: Mapped[str] = mapped_column(String(120))
message_id: Mapped[str | None] = mapped_column(String(500))
mail_message_id: Mapped[int | None] = mapped_column(ForeignKey("mail_messages.id"))
reason: Mapped[str] = mapped_column(String(80), index=True)
raw_xml_sha256: Mapped[str | None] = mapped_column(String(64), index=True)
existing_report_id: Mapped[int | None] = mapped_column(ForeignKey("reports.id"))
report_identifier: Mapped[str | None] = mapped_column(String(500), index=True)
reporting_org: Mapped[str | None] = mapped_column(String(255), index=True)
report_date: Mapped[date | None] = mapped_column(Date, index=True)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
class Alert(Base):
__tablename__ = "alerts"
id: Mapped[int] = mapped_column(primary_key=True)
fingerprint: Mapped[str] = mapped_column(String(500), unique=True, index=True)
inbox_id: Mapped[str] = mapped_column(String(120), index=True)
domain: Mapped[str] = mapped_column(String(255), index=True)
severity: Mapped[str] = mapped_column(String(40), index=True)
type: Mapped[str] = mapped_column(String(120), index=True)
title: Mapped[str] = mapped_column(String(500))
summary: Mapped[str] = mapped_column(Text)
details_json: Mapped[str] = mapped_column(Text, default="{}")
llm_summary: Mapped[str | None] = mapped_column(Text)
llm_risk: Mapped[str | None] = mapped_column(Text)
llm_recommended_action: Mapped[str | None] = mapped_column(Text)
status: Mapped[str] = mapped_column(String(40), default="open", index=True)
first_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
last_seen_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
class DailyStat(Base):
__tablename__ = "daily_stats"
__table_args__ = (UniqueConstraint("domain", "date", name="uq_daily_stat_domain_date"),)
id: Mapped[int] = mapped_column(primary_key=True)
domain: Mapped[str] = mapped_column(String(255), index=True)
date: Mapped[date] = mapped_column(Date, index=True)
total_messages: Mapped[int] = mapped_column(Integer, default=0)
dmarc_pass_count: Mapped[int] = mapped_column(Integer, default=0)
dmarc_fail_count: Mapped[int] = mapped_column(Integer, default=0)
spf_aligned_count: Mapped[int] = mapped_column(Integer, default=0)
spf_failed_count: Mapped[int] = mapped_column(Integer, default=0)
dkim_aligned_count: Mapped[int] = mapped_column(Integer, default=0)
dkim_failed_count: Mapped[int] = mapped_column(Integer, default=0)
unknown_source_count: Mapped[int] = mapped_column(Integer, default=0)
known_source_count: Mapped[int] = mapped_column(Integer, default=0)
quarantine_count: Mapped[int] = mapped_column(Integer, default=0)
reject_count: Mapped[int] = mapped_column(Integer, default=0)
top_reporters_json: Mapped[str] = mapped_column(Text, default="[]")
top_sources_json: Mapped[str] = mapped_column(Text, default="[]")
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow, onupdate=utcnow)
class LLMReport(Base):
__tablename__ = "llm_reports"
id: Mapped[int] = mapped_column(primary_key=True)
domain: Mapped[str] = mapped_column(String(255), index=True)
period_start: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
period_end: Mapped[datetime] = mapped_column(DateTime(timezone=True), index=True)
report_type: Mapped[str] = mapped_column(String(40), index=True)
input_json: Mapped[str] = mapped_column(Text)
output_json: Mapped[str] = mapped_column(Text)
plain_text: Mapped[str] = mapped_column(Text)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utcnow)