feat(security): isolate bounded work and support required auth actions

This commit is contained in:
2026-09-08 07:47:17 +02:00
parent a6d056a3df
commit dc1f244f17
23 changed files with 1628 additions and 21 deletions
+50 -6
View File
@@ -8,10 +8,11 @@ import tempfile
import time
import unittest
import zipfile
from datetime import datetime, timezone
from datetime import datetime, timedelta, timezone
from email import policy
from email.parser import BytesParser
from pathlib import Path
from types import SimpleNamespace
from unittest.mock import patch
import pyzipper
@@ -3545,13 +3546,17 @@ class ApiSmokeTests(unittest.TestCase):
self.assertEqual(len(jobs.json()["jobs"]), 1)
job_summary = jobs.json()["jobs"][0]
self.assertEqual(job_summary["campaign_version_id"], version_id)
self.assertNotIn("resolved_recipients", job_summary)
self.assertEqual(job_summary["resolved_recipients"]["to"][0]["email"], "recipient@example.org")
self.assertNotIn("attachments", job_summary)
detail = self.client.get(
f"/api/v1/campaigns/{campaign_id}/jobs/{job_summary['id']}",
headers=headers,
)
self.assertEqual(detail.status_code, 200, detail.text)
job = detail.json()["job"]
self.assertEqual(job_summary["resolved_recipients"], {
kind: job["resolved_recipients"][kind] for kind in ("to", "cc", "bcc")
})
self.assertEqual(job["resolved_recipients"]["to"][0]["email"], "recipient@example.org")
self.assertEqual(
{
@@ -4613,7 +4618,9 @@ class ApiSmokeTests(unittest.TestCase):
self.assertEqual(queued_summary.json()["status_counts"]["send"]["queued"], 2)
from govoplan_campaign.backend.db.models import Campaign, CampaignJob, CampaignVersion, SendAttempt
from govoplan_campaign.backend.sending.jobs import send_campaign_job
from govoplan_campaign.backend.sending.jobs import _begin_job_delivery_recovery, send_campaign_job
from govoplan_campaign.backend.services.delivery_recovery import job_recovery_metadata
from govoplan_core.core.runtime_coordination import DistributedLease, RuntimeNode, process_runtime_identity
with SessionLocal() as session:
jobs = (
@@ -4645,7 +4652,42 @@ class ApiSmokeTests(unittest.TestCase):
with SessionLocal() as session:
result = send_campaign_job(session, job_id=uncertain_job_id, use_rate_limit=False)
self.assertEqual(result.status, "outcome_unknown")
# Observing SENDING is not proof that its owner has stopped.
self.assertEqual(result.status, "already_sending")
job = session.get(CampaignJob, uncertain_job_id)
version = session.get(CampaignVersion, version_id)
recovery = _begin_job_delivery_recovery(
job=job, context=SimpleNamespace(version=version), claim_token=job.claim_token,
)
self.assertTrue(recovery.operation_id)
session.expire_all()
lease = session.query(DistributedLease).filter(
DistributedLease.resource_key == f"campaign:delivery:{job.tenant_id}:{job.id}",
).one()
lease.holder_node_id = "smoke-stopped-worker"
lease.holder_incarnation = "smoke-old-incarnation"
lease.expires_at = datetime.now(timezone.utc) - timedelta(minutes=1)
session.add(RuntimeNode(
installation_id=process_runtime_identity().installation_id,
node_id="smoke-stopped-worker", incarnation="smoke-old-incarnation",
role="worker", software_version="test", composition_hash="c" * 64,
state="stopped",
))
session.commit()
# Read the committed representation, just as an independent HTTP
# reader does (SQLite drops timezone objects during persistence).
session.expire_all()
metadata = job_recovery_metadata(session, [job])[job.id]["smtp"]
self.assertTrue(metadata["eligible"])
recovered = self.client.post(
f"/api/v1/campaigns/{campaign_id}/jobs/{uncertain_job_id}/recover-claim",
headers=headers,
json={"channel": "smtp", "expected_revision": metadata["revision"],
"note": "Fixture worker is confirmed stopped; inspect provider evidence next."},
)
self.assertEqual(recovered.status_code, 200, recovered.text)
self.assertTrue(recovered.json()["result"]["reconciliation_required"])
retry_unknown = self.client.post(
f"/api/v1/campaigns/{campaign_id}/jobs/retry",
@@ -4672,7 +4714,8 @@ class ApiSmokeTests(unittest.TestCase):
self.assertEqual(page.json()["total"], 2)
self.assertEqual(page.json()["pages"], 2)
self.assertEqual(page.json()["counts"]["send"]["outcome_unknown"], 1)
self.assertNotIn("resolved_recipients", page.json()["jobs"][0])
self.assertIn("resolved_recipients", page.json()["jobs"][0])
self.assertNotIn("attachments", page.json()["jobs"][0])
filtered_page = self.client.get(
f"/api/v1/campaigns/{campaign_id}/jobs",
@@ -4851,7 +4894,8 @@ class ApiSmokeTests(unittest.TestCase):
self.assertEqual(first_jobs.json()["total_unfiltered"], 1)
self.assertEqual(first_jobs.json()["review"]["required_count"], 0)
self.assertIn("reviewed", first_jobs.json()["jobs"][0])
self.assertNotIn("resolved_recipients", first_jobs.json()["jobs"][0])
self.assertEqual(first_jobs.json()["jobs"][0]["resolved_recipients"]["to"][0]["email"], "recipient-1@example.org")
self.assertNotIn("attachments", first_jobs.json()["jobs"][0])
first_csv = self.client.get(
f"/api/v1/campaigns/{campaign_id}/report/jobs.csv",