feat(campaigns): add approval fields and CampaignModificationRequest model
This commit is contained in:
@@ -8,7 +8,7 @@ from app.models.threat_actor import ThreatActor, ThreatActorTechnique
|
|||||||
from app.models.defensive_technique import DefensiveTechnique, DefensiveTechniqueMapping
|
from app.models.defensive_technique import DefensiveTechnique, DefensiveTechniqueMapping
|
||||||
from app.models.test_template_detection_rule import TestTemplateDetectionRule
|
from app.models.test_template_detection_rule import TestTemplateDetectionRule
|
||||||
from app.models.test_detection_result import TestDetectionResult
|
from app.models.test_detection_result import TestDetectionResult
|
||||||
from app.models.campaign import Campaign, CampaignTest
|
from app.models.campaign import Campaign, CampaignTest, CampaignModificationRequest
|
||||||
from app.models.compliance import ComplianceFramework, ComplianceControl, ComplianceControlMapping
|
from app.models.compliance import ComplianceFramework, ComplianceControl, ComplianceControlMapping
|
||||||
from app.models.coverage_snapshot import CoverageSnapshot, SnapshotTechniqueState
|
from app.models.coverage_snapshot import CoverageSnapshot, SnapshotTechniqueState
|
||||||
from app.models.jira_link import JiraLink, JiraLinkEntityType, JiraSyncDirection
|
from app.models.jira_link import JiraLink, JiraLinkEntityType, JiraSyncDirection
|
||||||
@@ -59,7 +59,7 @@ __all__ = [
|
|||||||
# Literal argument value
|
# Literal argument value
|
||||||
"TestTemplateDetectionRule", "TestDetectionResult",
|
"TestTemplateDetectionRule", "TestDetectionResult",
|
||||||
# Literal argument value
|
# Literal argument value
|
||||||
"Campaign", "CampaignTest",
|
"Campaign", "CampaignTest", "CampaignModificationRequest",
|
||||||
# Literal argument value
|
# Literal argument value
|
||||||
"ComplianceFramework", "ComplianceControl", "ComplianceControlMapping",
|
"ComplianceFramework", "ComplianceControl", "ComplianceControlMapping",
|
||||||
# Literal argument value
|
# Literal argument value
|
||||||
|
|||||||
@@ -75,6 +75,13 @@ class Campaign(Base):
|
|||||||
)
|
)
|
||||||
start_date = Column(DateTime, nullable=True) # campaign won't activate before this date
|
start_date = Column(DateTime, nullable=True) # campaign won't activate before this date
|
||||||
scheduled_at = Column(DateTime, nullable=True)
|
scheduled_at = Column(DateTime, nullable=True)
|
||||||
|
approved_by = Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
ForeignKey("users.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
approved_at = Column(DateTime, nullable=True)
|
||||||
|
rejection_reason = Column(Text, nullable=True)
|
||||||
# Assign completed_at = Column(DateTime, nullable=True)
|
# Assign completed_at = Column(DateTime, nullable=True)
|
||||||
completed_at = Column(DateTime, nullable=True)
|
completed_at = Column(DateTime, nullable=True)
|
||||||
# Assign target_platform = Column(String, nullable=True)
|
# Assign target_platform = Column(String, nullable=True)
|
||||||
@@ -229,3 +236,53 @@ class CampaignTest(Base):
|
|||||||
Index('ix_campaign_tests_campaign', 'campaign_id'),
|
Index('ix_campaign_tests_campaign', 'campaign_id'),
|
||||||
Index('ix_campaign_tests_test', 'test_id'),
|
Index('ix_campaign_tests_test', 'test_id'),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class CampaignModificationRequest(Base):
|
||||||
|
"""A lead's request to add or remove a test from an already-active campaign.
|
||||||
|
|
||||||
|
The underlying ``CampaignTest`` row is only created/deleted once a
|
||||||
|
manager approves the request — see ``approve_modification_request``
|
||||||
|
in ``campaign_crud_service.py``.
|
||||||
|
"""
|
||||||
|
__tablename__ = "campaign_modification_requests"
|
||||||
|
|
||||||
|
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||||
|
campaign_id = Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
ForeignKey("campaigns.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
requested_by = Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
ForeignKey("users.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
action = Column(String, nullable=False) # add_test, remove_test
|
||||||
|
test_id = Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
ForeignKey("tests.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
)
|
||||||
|
order_index = Column(Integer, nullable=True)
|
||||||
|
phase = Column(String, nullable=True)
|
||||||
|
justification = Column(Text, nullable=False)
|
||||||
|
status = Column(String, nullable=False, default="pending") # pending, approved, rejected
|
||||||
|
reviewed_by = Column(
|
||||||
|
UUID(as_uuid=True),
|
||||||
|
ForeignKey("users.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
)
|
||||||
|
reviewed_at = Column(DateTime, nullable=True)
|
||||||
|
review_notes = Column(Text, nullable=True)
|
||||||
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
|
|
||||||
|
campaign = relationship("Campaign")
|
||||||
|
test = relationship("Test")
|
||||||
|
requester = relationship("User", foreign_keys=[requested_by])
|
||||||
|
reviewer = relationship("User", foreign_keys=[reviewed_by])
|
||||||
|
|
||||||
|
__table_args__ = (
|
||||||
|
Index("ix_campaign_mod_requests_campaign", "campaign_id"),
|
||||||
|
Index("ix_campaign_mod_requests_status", "status"),
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user