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}