Files
DECNET/decnet/web/db/models/__init__.py
anti 2f47f67eef feat(creds): future-proof Credential storage model
Replaces the opaque Bounty.bounty_type='credential' path with a
dedicated `credentials` table whose schema is forward-compatible
across every auth-bearing service in the fleet. Hoisted indexed
columns (secret_sha256, principal, service, attacker_ip) carry the
universal reuse-analytics signal; service-specific JSON keys ride
in `fields`. Cross-service reuse queries become an indexed lookup
on secret_sha256 instead of JSON_EXTRACT scans.

Schema decisions baked in (per ANTI):
- New `Credential` table, not extension to Bounty
- Hoisted `principal` column for cross-service principal-reuse
- Standardized JSON keys: every payload carries secret_b64 +
  secret_printable + principal universally; service-specific extras
  (user, domain, dn, mech, …) ride alongside

The auth-helper SD-block emits the new shape natively. The ingester
forks at _extract_bounty:
- Native shape (SSH/Telnet, future emitters): secret_b64 present →
  direct upsert_credential
- Legacy shape (FTP/POP3/IMAP/SMTP today): username + password →
  adapter synthesizes secret_{b64,sha256,printable} on the fly,
  upserts into the same Credential table. Tracked as DEBT-039;
  one-shot bridge until those service templates migrate.

Defense-in-depth across five layers (input validation):
- C helper: bytes outside [0x20, 0x7f) collapse to '?', RFC 5424
  escape rules for \\, ", ]; b64 preserves exact bytes
- Ingester native branch: rejects malformed secret_b64 (regex), drops
  the credential row but keeps the underlying Log
- Ingester legacy adapter: same printable-ASCII filter as the C
  code; sha256 + b64 over the original utf-8 bytes (lossless, even
  when secret_printable is sanitized)
- DB column caps with truncation warning; sha256 always over the
  full pre-truncation bytes so reuse queries match across truncation
- JSON serialized with ensure_ascii=True so utf8mb4 columns stay
  safe even with non-ASCII service-specific keys

Bounty.bounty_type='credential' is no longer written. Pre-v1: no
historical backfill; existing rows stay untouched but unused.

595 tests pass; new tests cover the model + repo (upsert dedup,
null-principal independence, cross-service reuse, filters), both
ingester branches, b64 validation, sanitization preserving the
fingerprinting signal in b64.
2026-04-25 05:29:26 -04:00

249 lines
5.2 KiB
Python

"""
Database tables (SQLModel) and HTTP request/response shapes (Pydantic).
Split into topical modules for readability, but every symbol is re-exported
from this package so ``from decnet.web.db.models import X`` keeps working
everywhere — no importer needs to know which submodule a class lives in.
"""
from ._base import (
NullableDatetime,
NullableString,
_BIG_TEXT,
_normalize_null,
)
from .common import (
MessageResponse,
)
from .auth import (
AdminConfigResponse,
ChangePasswordRequest,
ConfigResponse,
CreateUserRequest,
DeploymentLimitRequest,
GlobalMutationIntervalRequest,
LoginRequest,
ResetUserPasswordRequest,
Token,
UpdateUserRoleRequest,
User,
UserResponse,
)
from .attackers import (
Attacker,
AttackerBehavior,
AttackersResponse,
SessionProfile,
SmtpTarget,
)
from .deploy import (
DeployIniRequest,
DeployResponse,
MutateIntervalRequest,
PurgeResponse,
)
from .health import (
ComponentHealth,
HealthResponse,
)
from .logs import (
Bounty,
BountyResponse,
Credential,
CredentialsResponse,
Log,
LogsResponse,
State,
StatsResponse,
)
from .swarm import (
DeckyShard,
DeckyShardView,
SwarmCheckResponse,
SwarmDeployRequest,
SwarmDeployResponse,
SwarmEnrolledBundle,
SwarmEnrollRequest,
SwarmHost,
SwarmHostHealth,
SwarmHostResult,
SwarmHostView,
SwarmTeardownRequest,
SwarmUpdaterBundle,
)
from .topology import (
LAN,
ArchetypeCatalogResponse,
ArchetypeEntry,
DeckyCreateRequest,
DeckyRow,
DeckyUpdateRequest,
DeployAcceptedResponse,
EdgeCreateRequest,
EdgeRow,
LANCreateRequest,
LANRow,
LANUpdateRequest,
MutationEnqueueRequest,
MutationEnqueueResponse,
MutationRow,
NextIPResponse,
NextSubnetResponse,
NotEditableResponse,
ReapReportResponse,
ServiceCatalogResponse,
Topology,
TopologyDecky,
TopologyDetail,
TopologyEdge,
TopologyGenerateRequest,
TopologyListResponse,
TopologyMutation,
TopologyStatusEvent,
TopologyStatusEventRow,
TopologySummary,
ValidationErrorResponse,
ValidationIssueResponse,
VersionConflictResponse,
)
from .updater import (
HostReleaseInfo,
HostReleasesResponse,
PushUpdateRequest,
PushUpdateResponse,
PushUpdateResult,
RollbackRequest,
RollbackResponse,
)
from .webhooks import (
SimpleEvent,
WebhookCreateRequest,
WebhookCreateResponse,
WebhookResponse,
WebhookSubscription,
WebhookTestResponse,
WebhookUpdateRequest,
)
from .workers import (
StartAllResponse,
StartFailure,
WorkerControlResponse,
WorkersResponse,
WorkerStatus,
)
__all__ = [
# _base
"NullableDatetime",
"NullableString",
"_BIG_TEXT",
"_normalize_null",
# common
"MessageResponse",
# auth
"AdminConfigResponse",
"ChangePasswordRequest",
"ConfigResponse",
"CreateUserRequest",
"DeploymentLimitRequest",
"GlobalMutationIntervalRequest",
"LoginRequest",
"ResetUserPasswordRequest",
"Token",
"UpdateUserRoleRequest",
"User",
"UserResponse",
# attackers
"Attacker",
"AttackerBehavior",
"AttackersResponse",
"SessionProfile",
"SmtpTarget",
# deploy
"DeployIniRequest",
"DeployResponse",
"MutateIntervalRequest",
"PurgeResponse",
# health
"ComponentHealth",
"HealthResponse",
# logs
"Bounty",
"BountyResponse",
"Credential",
"CredentialsResponse",
"Log",
"LogsResponse",
"State",
"StatsResponse",
# swarm
"DeckyShard",
"DeckyShardView",
"SwarmCheckResponse",
"SwarmDeployRequest",
"SwarmDeployResponse",
"SwarmEnrolledBundle",
"SwarmEnrollRequest",
"SwarmHost",
"SwarmHostHealth",
"SwarmHostResult",
"SwarmHostView",
"SwarmTeardownRequest",
"SwarmUpdaterBundle",
# topology
"LAN",
"ArchetypeCatalogResponse",
"ArchetypeEntry",
"DeckyCreateRequest",
"DeckyRow",
"DeckyUpdateRequest",
"DeployAcceptedResponse",
"EdgeCreateRequest",
"EdgeRow",
"LANCreateRequest",
"LANRow",
"LANUpdateRequest",
"MutationEnqueueRequest",
"MutationEnqueueResponse",
"MutationRow",
"NextIPResponse",
"NextSubnetResponse",
"NotEditableResponse",
"ReapReportResponse",
"ServiceCatalogResponse",
"Topology",
"TopologyDecky",
"TopologyDetail",
"TopologyEdge",
"TopologyGenerateRequest",
"TopologyListResponse",
"TopologyMutation",
"TopologyStatusEvent",
"TopologyStatusEventRow",
"TopologySummary",
"ValidationErrorResponse",
"ValidationIssueResponse",
"VersionConflictResponse",
# updater
"HostReleaseInfo",
"HostReleasesResponse",
"PushUpdateRequest",
"PushUpdateResponse",
"PushUpdateResult",
"RollbackRequest",
"RollbackResponse",
# webhooks
"SimpleEvent",
"WebhookCreateRequest",
"WebhookCreateResponse",
"WebhookResponse",
"WebhookSubscription",
"WebhookTestResponse",
"WebhookUpdateRequest",
# workers
"StartAllResponse",
"StartFailure",
"WorkerControlResponse",
"WorkersResponse",
"WorkerStatus",
]