1use std::collections::HashMap;
14use std::fs;
15use std::path::Path;
16use std::sync::Arc;
17
18use serde_json::Value;
19
20use crate::canonical::canonicalize;
21use crate::crypto::{b64decode, ed25519_verify, CryptoError};
22
23#[derive(Debug, thiserror::Error)]
24pub enum PluginError {
25 #[error("plugin I/O error: {0}")]
26 Io(String),
27 #[error("plugin parse error: {0}")]
28 Parse(String),
29 #[error("plugin signature invalid: {0}")]
30 BadSignature(String),
31 #[error("unknown plugin kind: {0}")]
32 UnknownKind(String),
33 #[error("crypto error: {0}")]
34 Crypto(String),
35}
36
37impl From<CryptoError> for PluginError {
38 fn from(e: CryptoError) -> Self {
39 PluginError::Crypto(e.to_string())
40 }
41}
42
43pub type NativeHandler = Arc<dyn Fn(&Value) -> Result<Value, String> + Send + Sync + 'static>;
45
46pub struct LoadedPlugin {
47 pub plugin_id: String,
48 pub actor_id: String,
49 pub kind: String,
50 pub capabilities: Vec<String>,
51 pub handlers: HashMap<String, NativeHandler>,
54}
55
56impl std::fmt::Debug for LoadedPlugin {
57 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
58 f.debug_struct("LoadedPlugin")
59 .field("plugin_id", &self.plugin_id)
60 .field("actor_id", &self.actor_id)
61 .field("kind", &self.kind)
62 .field("capabilities", &self.capabilities)
63 .field("handler_count", &self.handlers.len())
64 .finish()
65 }
66}
67
68pub struct PluginRegistry {
69 plugins: Vec<LoadedPlugin>,
70}
71
72impl PluginRegistry {
73 pub fn new() -> Self {
74 PluginRegistry {
75 plugins: Vec::new(),
76 }
77 }
78
79 pub fn load_native<P: AsRef<Path>>(
82 &mut self,
83 manifest_path: P,
84 handlers: HashMap<String, NativeHandler>,
85 ) -> Result<&LoadedPlugin, PluginError> {
86 let raw = fs::read_to_string(manifest_path.as_ref())
87 .map_err(|e| PluginError::Io(e.to_string()))?;
88 let manifest: Value = {
89 let yaml: serde_yaml::Value =
90 serde_yaml::from_str(&raw).map_err(|e| PluginError::Parse(e.to_string()))?;
91 serde_json::to_value(yaml).map_err(|e| PluginError::Parse(e.to_string()))?
92 };
93 let kind = manifest
94 .get("kind")
95 .and_then(|v| v.as_str())
96 .unwrap_or("")
97 .to_string();
98 if kind != "native" {
99 return Err(PluginError::UnknownKind(kind));
100 }
101 let plugin_id = manifest
102 .get("plugin_id")
103 .and_then(|v| v.as_str())
104 .unwrap_or("")
105 .to_string();
106 verify_signature_value(&manifest, &plugin_id)?;
107 let actor_id = manifest
108 .get("actor_id")
109 .and_then(|v| v.as_str())
110 .unwrap_or("")
111 .to_string();
112 let capability_names: Vec<String> = manifest
113 .get("capabilities")
114 .and_then(|v| v.as_array())
115 .map(|arr| {
116 arr.iter()
117 .filter_map(|v| v.get("name").and_then(|n| n.as_str()).map(str::to_string))
118 .collect()
119 })
120 .unwrap_or_default();
121 let plugin = LoadedPlugin {
122 plugin_id,
123 actor_id,
124 kind,
125 capabilities: capability_names,
126 handlers,
127 };
128 self.plugins.push(plugin);
129 Ok(self.plugins.last().unwrap())
130 }
131
132 pub fn list(&self) -> &[LoadedPlugin] {
133 &self.plugins
134 }
135
136 pub fn invoke(
137 &self,
138 plugin_id: &str,
139 capability: &str,
140 request: &Value,
141 ) -> Result<Value, PluginError> {
142 let plugin = self
143 .plugins
144 .iter()
145 .find(|p| p.plugin_id == plugin_id)
146 .ok_or_else(|| PluginError::Parse(format!("plugin not loaded: {}", plugin_id)))?;
147 let handler = plugin.handlers.get(capability).ok_or_else(|| {
148 PluginError::Parse(format!("no handler for capability: {}", capability))
149 })?;
150 handler(request).map_err(PluginError::Parse)
151 }
152}
153
154pub fn verify_plugin_signature<P: AsRef<Path>>(manifest_path: P) -> Result<String, PluginError> {
156 let raw =
157 fs::read_to_string(manifest_path.as_ref()).map_err(|e| PluginError::Io(e.to_string()))?;
158 let yaml: serde_yaml::Value =
159 serde_yaml::from_str(&raw).map_err(|e| PluginError::Parse(e.to_string()))?;
160 let manifest: Value =
161 serde_json::to_value(yaml).map_err(|e| PluginError::Parse(e.to_string()))?;
162 let plugin_id = manifest
163 .get("plugin_id")
164 .and_then(|v| v.as_str())
165 .unwrap_or("")
166 .to_string();
167 verify_signature_value(&manifest, &plugin_id)?;
168 Ok(plugin_id)
169}
170
171fn verify_signature_value(manifest: &Value, plugin_id: &str) -> Result<(), PluginError> {
172 let sig_b64 = manifest
173 .get("signature")
174 .and_then(|s| s.get("signature"))
175 .and_then(|v| v.as_str())
176 .ok_or_else(|| PluginError::BadSignature(plugin_id.to_string()))?
177 .to_string();
178 let ident_pub = manifest
179 .get("identity_pub")
180 .and_then(|v| v.as_str())
181 .ok_or_else(|| PluginError::BadSignature(plugin_id.to_string()))?
182 .to_string();
183
184 let mut cleared = manifest.clone();
186 if let Some(sig) = cleared.get_mut("signature").and_then(|s| s.as_object_mut()) {
187 sig.insert("signature".to_string(), Value::String(String::new()));
188 }
189 let canonical = canonicalize(&cleared).map_err(|e| PluginError::Parse(e.to_string()))?;
190 let sig_bytes = b64decode(&sig_b64)?;
191 let pubkey_bytes = b64decode(&ident_pub)?;
192 ed25519_verify(&pubkey_bytes, canonical.as_bytes(), &sig_bytes)
193 .map_err(|_| PluginError::BadSignature(plugin_id.to_string()))?;
194 Ok(())
195}
196
197impl Default for PluginRegistry {
198 fn default() -> Self {
199 PluginRegistry::new()
200 }
201}