p47h_engine/
wrappers.rs

1use core_identity::Identity;
2use core_policy::{Action, Policy, PolicyRule, Resource};
3use wasm_bindgen::prelude::*;
4
5/// WASM wrapper for Identity
6#[wasm_bindgen]
7pub struct WasmIdentity {
8    inner: Identity,
9}
10
11#[wasm_bindgen]
12impl WasmIdentity {
13    /// Generate a new identity
14    #[wasm_bindgen(constructor)]
15    pub fn new() -> Result<WasmIdentity, JsValue> {
16        let mut rng = rand::thread_rng();
17        let identity = Identity::generate(&mut rng)
18            .map_err(|e| JsValue::from_str(&format!("Failed to generate identity: {}", e)))?;
19
20        Ok(WasmIdentity { inner: identity })
21    }
22
23    /// Get the public key hash as hex string
24    #[wasm_bindgen(js_name = publicKeyHash)]
25    pub fn public_key_hash(&self) -> String {
26        hex::encode(self.inner.public_key_hash())
27    }
28
29    /// Get the DID (Decentralized Identifier)
30    #[wasm_bindgen(js_name = getDid)]
31    pub fn get_did(&self) -> String {
32        format!("did:p47h:{}", self.public_key_hash())
33    }
34}
35
36/// WASM wrapper for Policy
37#[wasm_bindgen]
38pub struct WasmPolicy {
39    inner: Policy,
40}
41
42#[wasm_bindgen]
43impl WasmPolicy {
44    /// Create a new policy
45    #[wasm_bindgen(constructor)]
46    pub fn new(name: &str, ttl_seconds: u64) -> Result<WasmPolicy, JsValue> {
47        // Use 0 as timestamp for WASM compatibility
48        // In production, timestamp should be provided by the server or JS Date.now()
49        let now = 0;
50
51        let policy = Policy::new(name, ttl_seconds, now)
52            .map_err(|e| JsValue::from_str(&format!("Failed to create policy: {}", e)))?;
53
54        Ok(WasmPolicy { inner: policy })
55    }
56
57    /// Add a rule to the policy
58    #[wasm_bindgen(js_name = addRule)]
59    pub fn add_rule(&mut self, peer_id: &str, action: &str, resource: &str) -> Result<(), JsValue> {
60        let action = match action {
61            "read" => Action::Read,
62            "write" => Action::Write,
63            "execute" => Action::Execute,
64            "all" => Action::All,
65            _ => return Err(JsValue::from_str(&format!("Invalid action: {}", action))),
66        };
67
68        let resource = if resource == "*" {
69            Resource::All
70        } else {
71            // Simple resource parsing for wrapper
72            Resource::File(resource.to_string())
73        };
74
75        let rule = PolicyRule::new(peer_id.to_string(), action, resource);
76
77        // Since Policy fields are private, we need to rebuild the policy with the new rule
78        // Get current policy data
79        let current_name = self.inner.name().to_string();
80        let current_issued_at = self.inner.issued_at();
81        let current_valid_until = self.inner.valid_until();
82        let ttl = current_valid_until.saturating_sub(current_issued_at);
83
84        // Collect existing rules and add the new one
85        let mut all_rules = self.inner.rules().to_vec();
86        all_rules.push(rule);
87
88        // Rebuild policy from scratch
89        let mut new_policy = Policy::new(current_name, ttl, current_issued_at)
90            .map_err(|e| JsValue::from_str(&format!("Failed to create policy: {}", e)))?;
91
92        // Add all rules
93        for r in all_rules {
94            new_policy = new_policy
95                .add_rule(r)
96                .map_err(|e| JsValue::from_str(&format!("Failed to add rule: {}", e)))?;
97        }
98
99        // Copy metadata
100        for (k, v) in self.inner.metadata() {
101            new_policy = new_policy.with_metadata(k.clone(), v.clone());
102        }
103
104        self.inner = new_policy;
105
106        Ok(())
107    }
108
109    /// Get policy name
110    #[wasm_bindgen(getter)]
111    pub fn name(&self) -> String {
112        self.inner.name().to_string()
113    }
114
115    /// Get number of rules
116    #[wasm_bindgen(js_name = ruleCount)]
117    pub fn rule_count(&self) -> usize {
118        self.inner.rules().len()
119    }
120
121    /// Get the Merkle root hash of the policy
122    ///
123    /// Calculates the hash of the canonical TOML representation of the policy.
124    /// This serves as the Merkle root for synchronization verification.
125    #[wasm_bindgen(js_name = getRootHash)]
126    pub fn get_root_hash(&self) -> Result<String, JsValue> {
127        let toml = self
128            .inner
129            .to_toml()
130            .map_err(|e| JsValue::from_str(&format!("Failed to serialize policy: {}", e)))?;
131
132        let digest = core_identity::hash::hash(toml.as_bytes());
133        Ok(hex::encode(digest))
134    }
135}
136
137impl WasmPolicy {
138    /// Internal access to the inner policy
139    pub fn get_inner(&self) -> &Policy {
140        &self.inner
141    }
142}