Skip to main content

authority_delegation/
capability.rs

1use schemars::JsonSchema;
2use serde::{Deserialize, Serialize};
3
4use crate::error::{
5    require_non_empty, require_non_empty_slice, AuthorityValidationError, AuthorityValidationResult,
6};
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
9#[serde(rename_all = "snake_case")]
10pub enum CapabilityBlastRadiusCeilingV1 {
11    SingleTenant,
12    SingleService,
13    Regional,
14    Global,
15}
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
18#[serde(rename_all = "snake_case")]
19pub enum AuthorityChainValidityStateV1 {
20    Valid,
21    Expired,
22    Revoked,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
26#[serde(rename_all = "snake_case")]
27pub enum CapabilityDisclosureCeilingV1 {
28    Internal,
29    RedactedStructuredOnly,
30}
31
32#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
33pub struct CapabilityClassV1 {
34    pub schema_version: String,
35    pub capability_class_id: String,
36    pub governed_effect_families: Vec<String>,
37    pub blast_radius_ceiling: CapabilityBlastRadiusCeilingV1,
38    pub disclosure_ceiling: CapabilityDisclosureCeilingV1,
39    pub required_review_class: String,
40    pub dual_control_required: bool,
41    pub max_delegation_depth: i64,
42}
43
44impl CapabilityClassV1 {
45    #[allow(clippy::too_many_arguments)]
46    pub fn new(
47        capability_class_id: impl Into<String>,
48        governed_effect_families: Vec<String>,
49        blast_radius_ceiling: CapabilityBlastRadiusCeilingV1,
50        disclosure_ceiling: CapabilityDisclosureCeilingV1,
51        required_review_class: impl Into<String>,
52        dual_control_required: bool,
53        max_delegation_depth: i64,
54    ) -> Result<Self, AuthorityValidationError> {
55        let value = Self {
56            schema_version: "CapabilityClassV1".to_string(),
57            capability_class_id: capability_class_id.into(),
58            governed_effect_families,
59            blast_radius_ceiling,
60            disclosure_ceiling,
61            required_review_class: required_review_class.into(),
62            dual_control_required,
63            max_delegation_depth,
64        };
65        value.validate()?;
66        Ok(value)
67    }
68
69    pub fn validate(&self) -> AuthorityValidationResult {
70        require_non_empty(&self.capability_class_id, "capability_class_id")?;
71        require_non_empty_slice(&self.governed_effect_families, "governed_effect_families")?;
72        require_non_empty(&self.required_review_class, "required_review_class")?;
73        if self.max_delegation_depth < 0 {
74            return Err(AuthorityValidationError::InvalidState(
75                "max_delegation_depth",
76            ));
77        }
78        Ok(())
79    }
80}
81
82#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
83pub struct AuthorityLeaseV1 {
84    pub schema_version: String,
85    pub authority_lease_id: String,
86    pub capability_class_id: String,
87    pub grantor: String,
88    pub grantee: String,
89    pub scope: String,
90    pub time_window: String,
91    pub allowed_effect_families: Vec<String>,
92    pub hard_ceilings: Vec<String>,
93    pub revocation_conditions: Vec<String>,
94    pub required_receipts: Vec<String>,
95}
96
97impl AuthorityLeaseV1 {
98    #[allow(clippy::too_many_arguments)]
99    pub fn new(
100        authority_lease_id: impl Into<String>,
101        capability_class_id: impl Into<String>,
102        grantor: impl Into<String>,
103        grantee: impl Into<String>,
104        scope: impl Into<String>,
105        time_window: impl Into<String>,
106        allowed_effect_families: Vec<String>,
107        hard_ceilings: Vec<String>,
108        revocation_conditions: Vec<String>,
109        required_receipts: Vec<String>,
110    ) -> Result<Self, AuthorityValidationError> {
111        let value = Self {
112            schema_version: "AuthorityLeaseV1".to_string(),
113            authority_lease_id: authority_lease_id.into(),
114            capability_class_id: capability_class_id.into(),
115            grantor: grantor.into(),
116            grantee: grantee.into(),
117            scope: scope.into(),
118            time_window: time_window.into(),
119            allowed_effect_families,
120            hard_ceilings,
121            revocation_conditions,
122            required_receipts,
123        };
124        value.validate()?;
125        Ok(value)
126    }
127
128    pub fn validate(&self) -> AuthorityValidationResult {
129        require_non_empty(&self.authority_lease_id, "authority_lease_id")?;
130        require_non_empty(&self.capability_class_id, "capability_class_id")?;
131        require_non_empty(&self.grantor, "grantor")?;
132        require_non_empty(&self.grantee, "grantee")?;
133        require_non_empty(&self.scope, "scope")?;
134        require_non_empty(&self.time_window, "time_window")?;
135        require_non_empty_slice(&self.allowed_effect_families, "allowed_effect_families")?;
136        require_non_empty_slice(&self.required_receipts, "required_receipts")?;
137        Ok(())
138    }
139}
140
141#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
142pub struct DelegationBundleV1 {
143    pub schema_version: String,
144    pub delegation_bundle_id: String,
145    pub authority_lease_id: String,
146    pub delegated_rights: Vec<String>,
147    pub reduced_ceilings: Vec<String>,
148    pub replay_requirements: Vec<String>,
149    pub further_delegation_permitted: bool,
150}
151
152impl DelegationBundleV1 {
153    #[allow(clippy::too_many_arguments)]
154    pub fn new(
155        delegation_bundle_id: impl Into<String>,
156        authority_lease_id: impl Into<String>,
157        delegated_rights: Vec<String>,
158        reduced_ceilings: Vec<String>,
159        replay_requirements: Vec<String>,
160        further_delegation_permitted: bool,
161    ) -> Result<Self, AuthorityValidationError> {
162        let value = Self {
163            schema_version: "DelegationBundleV1".to_string(),
164            delegation_bundle_id: delegation_bundle_id.into(),
165            authority_lease_id: authority_lease_id.into(),
166            delegated_rights,
167            reduced_ceilings,
168            replay_requirements,
169            further_delegation_permitted,
170        };
171        value.validate()?;
172        Ok(value)
173    }
174
175    pub fn validate(&self) -> AuthorityValidationResult {
176        require_non_empty(&self.delegation_bundle_id, "delegation_bundle_id")?;
177        require_non_empty(&self.authority_lease_id, "authority_lease_id")?;
178        require_non_empty_slice(&self.delegated_rights, "delegated_rights")?;
179        require_non_empty_slice(&self.replay_requirements, "replay_requirements")?;
180        Ok(())
181    }
182}
183
184#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
185pub struct AuthorityChainV1 {
186    pub schema_version: String,
187    pub authority_chain_id: String,
188    pub local_principal: String,
189    pub delegated_principals: Vec<String>,
190    pub external_principals: Vec<String>,
191    pub approval_refs: Vec<String>,
192    pub current_validity_state: AuthorityChainValidityStateV1,
193}
194
195impl AuthorityChainV1 {
196    #[allow(clippy::too_many_arguments)]
197    pub fn new(
198        authority_chain_id: impl Into<String>,
199        local_principal: impl Into<String>,
200        delegated_principals: Vec<String>,
201        external_principals: Vec<String>,
202        approval_refs: Vec<String>,
203        current_validity_state: AuthorityChainValidityStateV1,
204    ) -> Result<Self, AuthorityValidationError> {
205        let value = Self {
206            schema_version: "AuthorityChainV1".to_string(),
207            authority_chain_id: authority_chain_id.into(),
208            local_principal: local_principal.into(),
209            delegated_principals,
210            external_principals,
211            approval_refs,
212            current_validity_state,
213        };
214        value.validate()?;
215        Ok(value)
216    }
217
218    pub fn validate(&self) -> AuthorityValidationResult {
219        require_non_empty(&self.authority_chain_id, "authority_chain_id")?;
220        require_non_empty(&self.local_principal, "local_principal")?;
221        require_non_empty_slice(&self.approval_refs, "approval_refs")?;
222        Ok(())
223    }
224}