Skip to main content

profile_runtime/
profile_set.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3use stack_ids::{
4    AccessPurposeMatrixId, ApplicabilityContextId, ApprovalMatrixId, AuditExtractionPolicyId,
5    ConflictClassCatalogId, ContinuityPolicyProfileId, CrossBoundaryTransferClassId,
6    DelegationMatrixId, DelegationPolicyProfileId, EffectPolicyProfileId, EscalationClockPolicyId,
7    EvidenceCollectionPlanId, HazardLibraryId, HazardScenarioId, IncidentTaxonomyId,
8    LocalityExceptionId, MitigationPlaybookId, MonitorCatalogId, PagerRouteProfileId,
9    PrivacyRetentionProfileId, ProfileExceptionBundleId, ProfileSetId, RecertificationScheduleId,
10    RedactionRuleSetId, RegulatoryRegimeProfileId, ReleasePolicyProfileId, RequirementControlMapId,
11    ResidencyPolicyProfileId, RoleCatalogId, SeverityMatrixId, TenantBoundaryProfileId,
12    VendorCertificationAdapterId, VendorEvidenceTranslationId, VendorRevocationHandlingId,
13    VendorTrustRootBindingId,
14};
15
16pub const PROFILE_SET_V1_SCHEMA: &str = "profile_set_v1";
17
18#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema, Default)]
19pub struct ProfileRefGroupV1 {
20    #[serde(default, skip_serializing_if = "Option::is_none")]
21    pub privacy_retention_profile_id: Option<PrivacyRetentionProfileId>,
22    #[serde(default, skip_serializing_if = "Vec::is_empty")]
23    pub redaction_rule_set_ids: Vec<RedactionRuleSetId>,
24    #[serde(default, skip_serializing_if = "Option::is_none")]
25    pub access_purpose_matrix_id: Option<AccessPurposeMatrixId>,
26    #[serde(default, skip_serializing_if = "Option::is_none")]
27    pub audit_extraction_policy_id: Option<AuditExtractionPolicyId>,
28    #[serde(default, skip_serializing_if = "Option::is_none")]
29    pub residency_policy_profile_id: Option<ResidencyPolicyProfileId>,
30    #[serde(default, skip_serializing_if = "Option::is_none")]
31    pub tenant_boundary_profile_id: Option<TenantBoundaryProfileId>,
32    #[serde(default, skip_serializing_if = "Vec::is_empty")]
33    pub cross_boundary_transfer_class_ids: Vec<CrossBoundaryTransferClassId>,
34    #[serde(default, skip_serializing_if = "Vec::is_empty")]
35    pub locality_exception_ids: Vec<LocalityExceptionId>,
36    #[serde(default, skip_serializing_if = "Option::is_none")]
37    pub role_catalog_id: Option<RoleCatalogId>,
38    #[serde(default, skip_serializing_if = "Option::is_none")]
39    pub delegation_matrix_id: Option<DelegationMatrixId>,
40    #[serde(default, skip_serializing_if = "Option::is_none")]
41    pub approval_matrix_id: Option<ApprovalMatrixId>,
42    #[serde(default, skip_serializing_if = "Option::is_none")]
43    pub conflict_class_catalog_id: Option<ConflictClassCatalogId>,
44    #[serde(default, skip_serializing_if = "Option::is_none")]
45    pub regulatory_regime_profile_id: Option<RegulatoryRegimeProfileId>,
46    #[serde(default, skip_serializing_if = "Option::is_none")]
47    pub requirement_control_map_id: Option<RequirementControlMapId>,
48    #[serde(default, skip_serializing_if = "Option::is_none")]
49    pub evidence_collection_plan_id: Option<EvidenceCollectionPlanId>,
50    #[serde(default, skip_serializing_if = "Option::is_none")]
51    pub recertification_schedule_id: Option<RecertificationScheduleId>,
52    #[serde(default, skip_serializing_if = "Option::is_none")]
53    pub hazard_library_id: Option<HazardLibraryId>,
54    #[serde(default, skip_serializing_if = "Vec::is_empty")]
55    pub hazard_scenario_ids: Vec<HazardScenarioId>,
56    #[serde(default, skip_serializing_if = "Option::is_none")]
57    pub monitor_catalog_id: Option<MonitorCatalogId>,
58    #[serde(default, skip_serializing_if = "Vec::is_empty")]
59    pub mitigation_playbook_ids: Vec<MitigationPlaybookId>,
60    #[serde(default, skip_serializing_if = "Option::is_none")]
61    pub vendor_certification_adapter_id: Option<VendorCertificationAdapterId>,
62    #[serde(default, skip_serializing_if = "Option::is_none")]
63    pub vendor_evidence_translation_id: Option<VendorEvidenceTranslationId>,
64    #[serde(default, skip_serializing_if = "Option::is_none")]
65    pub vendor_trust_root_binding_id: Option<VendorTrustRootBindingId>,
66    #[serde(default, skip_serializing_if = "Option::is_none")]
67    pub vendor_revocation_handling_id: Option<VendorRevocationHandlingId>,
68    #[serde(default, skip_serializing_if = "Option::is_none")]
69    pub incident_taxonomy_id: Option<IncidentTaxonomyId>,
70    #[serde(default, skip_serializing_if = "Option::is_none")]
71    pub severity_matrix_id: Option<SeverityMatrixId>,
72    #[serde(default, skip_serializing_if = "Option::is_none")]
73    pub pager_route_profile_id: Option<PagerRouteProfileId>,
74    #[serde(default, skip_serializing_if = "Option::is_none")]
75    pub escalation_clock_policy_id: Option<EscalationClockPolicyId>,
76    #[serde(default, skip_serializing_if = "Option::is_none")]
77    pub effect_policy_profile_id: Option<EffectPolicyProfileId>,
78    #[serde(default, skip_serializing_if = "Option::is_none")]
79    pub delegation_policy_profile_id: Option<DelegationPolicyProfileId>,
80    #[serde(default, skip_serializing_if = "Option::is_none")]
81    pub release_policy_profile_id: Option<ReleasePolicyProfileId>,
82    #[serde(default, skip_serializing_if = "Option::is_none")]
83    pub continuity_policy_profile_id: Option<ContinuityPolicyProfileId>,
84}
85
86impl ProfileRefGroupV1 {
87    /// Returns the deduplicated set of profile references carried by this group.
88    pub fn all_profile_refs(&self) -> Vec<String> {
89        let mut refs = Vec::new();
90        macro_rules! push_opt {
91            ($field:expr) => {
92                if let Some(value) = &$field {
93                    refs.push(value.to_string());
94                }
95            };
96        }
97        macro_rules! push_vec {
98            ($field:expr) => {
99                refs.extend($field.iter().map(ToString::to_string));
100            };
101        }
102
103        push_opt!(self.privacy_retention_profile_id);
104        push_vec!(self.redaction_rule_set_ids);
105        push_opt!(self.access_purpose_matrix_id);
106        push_opt!(self.audit_extraction_policy_id);
107        push_opt!(self.residency_policy_profile_id);
108        push_opt!(self.tenant_boundary_profile_id);
109        push_vec!(self.cross_boundary_transfer_class_ids);
110        push_vec!(self.locality_exception_ids);
111        push_opt!(self.role_catalog_id);
112        push_opt!(self.delegation_matrix_id);
113        push_opt!(self.approval_matrix_id);
114        push_opt!(self.conflict_class_catalog_id);
115        push_opt!(self.regulatory_regime_profile_id);
116        push_opt!(self.requirement_control_map_id);
117        push_opt!(self.evidence_collection_plan_id);
118        push_opt!(self.recertification_schedule_id);
119        push_opt!(self.hazard_library_id);
120        push_vec!(self.hazard_scenario_ids);
121        push_opt!(self.monitor_catalog_id);
122        push_vec!(self.mitigation_playbook_ids);
123        push_opt!(self.vendor_certification_adapter_id);
124        push_opt!(self.vendor_evidence_translation_id);
125        push_opt!(self.vendor_trust_root_binding_id);
126        push_opt!(self.vendor_revocation_handling_id);
127        push_opt!(self.incident_taxonomy_id);
128        push_opt!(self.severity_matrix_id);
129        push_opt!(self.pager_route_profile_id);
130        push_opt!(self.escalation_clock_policy_id);
131        push_opt!(self.effect_policy_profile_id);
132        push_opt!(self.delegation_policy_profile_id);
133        push_opt!(self.release_policy_profile_id);
134        push_opt!(self.continuity_policy_profile_id);
135
136        refs.sort();
137        refs.dedup();
138        refs
139    }
140}
141
142#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
143pub struct ProfileOmissionV1 {
144    pub family: String,
145    pub reason: String,
146}
147
148#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
149pub struct ProfileSetV1 {
150    pub schema_version: String,
151    pub profile_set_id: ProfileSetId,
152    pub applicability_context_ref: ApplicabilityContextId,
153    pub profile_refs: ProfileRefGroupV1,
154    #[serde(default, skip_serializing_if = "Vec::is_empty")]
155    pub source_admission_refs: Vec<String>,
156    #[serde(default, skip_serializing_if = "Vec::is_empty")]
157    pub omissions: Vec<ProfileOmissionV1>,
158    #[serde(default, skip_serializing_if = "Vec::is_empty")]
159    pub active_exception_refs: Vec<ProfileExceptionBundleId>,
160    #[serde(default, skip_serializing_if = "Vec::is_empty")]
161    pub normalized_profile_order: Vec<String>,
162}
163
164impl ProfileSetV1 {
165    /// Creates a profile set and captures its initial normalized profile ordering.
166    pub fn new(
167        applicability_context_ref: ApplicabilityContextId,
168        profile_refs: ProfileRefGroupV1,
169        source_admission_refs: Vec<String>,
170    ) -> Self {
171        let normalized_profile_order = profile_refs.all_profile_refs();
172        Self {
173            schema_version: PROFILE_SET_V1_SCHEMA.into(),
174            profile_set_id: ProfileSetId::generate(),
175            applicability_context_ref,
176            profile_refs,
177            source_admission_refs,
178            omissions: Vec::new(),
179            active_exception_refs: Vec::new(),
180            normalized_profile_order,
181        }
182    }
183
184    /// Recomputes the normalized profile ordering after in-memory edits.
185    pub fn refresh_normalized_profile_order(&mut self) {
186        self.normalized_profile_order = self.profile_refs.all_profile_refs();
187    }
188
189    /// Returns the deduplicated profile references visible through this set.
190    pub fn all_profile_refs(&self) -> Vec<String> {
191        self.profile_refs.all_profile_refs()
192    }
193}