authorization.rs (4450B)
1 //! Current signing authorization, separate from historical plan integrity. 2 3 use radroots_event::contract::{EventAuthoringPolicy, EventStability, event_contract}; 4 use radroots_event_codec::authoring::AuthoredEventPlan; 5 6 use crate::{Actor, actor::ActorSource}; 7 8 /// Current policy decision for one historically valid authored plan. 9 #[non_exhaustive] 10 #[derive(Clone, Copy, Debug, PartialEq, Eq)] 11 pub enum CurrentAuthoringDecision { 12 Allowed, 13 AllowedDeprecated { warning_code: &'static str }, 14 Blocked { code: &'static str }, 15 Revoked { code: &'static str }, 16 } 17 18 /// Host or registry authority for current signing policy. 19 pub trait CurrentAuthoringAuthority: Send + Sync { 20 fn evaluate(&self, plan: &AuthoredEventPlan) -> CurrentAuthoringDecision; 21 } 22 23 /// Current immutable registry policy. 24 #[derive(Clone, Copy, Debug, Default)] 25 pub struct CurrentRegistryAuthority; 26 27 impl CurrentAuthoringAuthority for CurrentRegistryAuthority { 28 fn evaluate(&self, plan: &AuthoredEventPlan) -> CurrentAuthoringDecision { 29 let Some(contract) = event_contract(plan.body().contract().contract_id().as_str()) else { 30 return CurrentAuthoringDecision::Blocked { 31 code: "contract_not_current", 32 }; 33 }; 34 if contract.authoring_policy() == EventAuthoringPolicy::ReadOnly { 35 return CurrentAuthoringDecision::Revoked { 36 code: "contract_read_only", 37 }; 38 } 39 match contract.stability { 40 EventStability::Stable => CurrentAuthoringDecision::Allowed, 41 EventStability::Experimental => CurrentAuthoringDecision::AllowedDeprecated { 42 warning_code: "contract_experimental", 43 }, 44 } 45 } 46 } 47 48 /// Whether a request explicitly accepts a currently deprecated contract. 49 #[non_exhaustive] 50 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 51 #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] 52 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 53 pub enum DeprecatedPlanPolicy { 54 #[default] 55 Deny, 56 Allow, 57 } 58 59 /// Host-owned policy limiting which validated actor provenance may sign. 60 #[non_exhaustive] 61 #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] 62 #[cfg_attr(feature = "serde", serde(rename_all = "snake_case"))] 63 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] 64 pub enum ManagedSigningPolicy { 65 #[default] 66 AnyValidatedSource, 67 AccountBackedOnly, 68 LocalAccountOnly, 69 } 70 71 impl ManagedSigningPolicy { 72 #[must_use] 73 pub const fn permits(self, actor: &Actor) -> bool { 74 match self { 75 Self::AnyValidatedSource => true, 76 Self::AccountBackedOnly => actor.source().account_id().is_some(), 77 Self::LocalAccountOnly => matches!(actor.source(), ActorSource::LocalAccount(_)), 78 } 79 } 80 } 81 82 #[cfg(test)] 83 mod tests { 84 use radroots_event::contract::AuthorRole; 85 use radroots_identity::{AccountId, PublicKey}; 86 87 use super::*; 88 89 const KEY: &str = "585591529da0bab31b3b1b1f986611cf5f435dca84f978c89ee8a40cca7103df"; 90 91 #[test] 92 fn managed_signing_policy_covers_every_provenance_class() { 93 let public_key = PublicKey::from_hex(KEY).expect("public key"); 94 let account_id = AccountId::from_hex(KEY).expect("account ID"); 95 let explicit = Actor::new( 96 public_key, 97 ActorSource::ExplicitPublicKey, 98 [AuthorRole::Any], 99 ) 100 .expect("explicit actor"); 101 let local = Actor::new( 102 public_key, 103 ActorSource::LocalAccount(account_id), 104 [AuthorRole::Any], 105 ) 106 .expect("local actor"); 107 let remote = Actor::new( 108 public_key, 109 ActorSource::RemoteSigner(account_id), 110 [AuthorRole::Any], 111 ) 112 .expect("remote actor"); 113 114 for actor in [&explicit, &local, &remote] { 115 assert!(ManagedSigningPolicy::AnyValidatedSource.permits(actor)); 116 } 117 assert!(!ManagedSigningPolicy::AccountBackedOnly.permits(&explicit)); 118 assert!(ManagedSigningPolicy::AccountBackedOnly.permits(&local)); 119 assert!(ManagedSigningPolicy::AccountBackedOnly.permits(&remote)); 120 assert!(!ManagedSigningPolicy::LocalAccountOnly.permits(&explicit)); 121 assert!(ManagedSigningPolicy::LocalAccountOnly.permits(&local)); 122 assert!(!ManagedSigningPolicy::LocalAccountOnly.permits(&remote)); 123 } 124 }