This commit is contained in:
Sameh Abouel-saad
2025-06-06 05:31:03 +03:00
parent 203cde1cba
commit 6f42e5ab8d
10 changed files with 1582 additions and 400 deletions

View File

@@ -5,6 +5,7 @@ use wasm_bindgen::prelude::*;
use serde::{Deserialize, Serialize};
use sigsocket_client::{SigSocketClient, SignRequest, SignRequestHandler, Result as SigSocketResult, SigSocketError};
use web_sys::console;
use base64::prelude::*;
use crate::vault_bindings::{get_workspace_default_public_key, get_current_keyspace_name, is_unlocked, sign_with_default_keypair};
@@ -31,7 +32,32 @@ impl ExtensionNotificationHandler {
impl SignRequestHandler for ExtensionNotificationHandler {
fn handle_sign_request(&self, request: &SignRequest) -> SigSocketResult<Vec<u8>> {
// Create event object for JavaScript
console_log!("📨 WASM: Handling sign request: {}", request.id);
// First, store the request in the WASM client
let store_result = SIGSOCKET_CLIENT.with(|c| {
let mut client_opt = c.borrow_mut();
if let Some(client) = client_opt.as_mut() {
// Get the connected public key as the target
if let Some(target_public_key) = client.connected_public_key() {
client.add_pending_request(request.clone(), target_public_key.to_string());
console_log!("✅ WASM: Stored sign request: {}", request.id);
Ok(())
} else {
Err(SigSocketError::Other("No connected public key".to_string()))
}
} else {
Err(SigSocketError::Other("No SigSocket client available".to_string()))
}
});
// If storage failed, return error
if let Err(e) = store_result {
console_log!("❌ WASM: Failed to store request: {:?}", e);
return Err(e);
}
// Create event object for JavaScript notification
let event = js_sys::Object::new();
js_sys::Reflect::set(&event, &"type".into(), &"sign_request".into())
.map_err(|_| SigSocketError::Other("Failed to set event type".to_string()))?;
@@ -40,17 +66,16 @@ impl SignRequestHandler for ExtensionNotificationHandler {
js_sys::Reflect::set(&event, &"message".into(), &request.message.clone().into())
.map_err(|_| SigSocketError::Other("Failed to set message".to_string()))?;
// Store the request in our pending requests (this will be done by the client)
// and notify the extension
// Notify the extension
match self.callback.call1(&wasm_bindgen::JsValue::NULL, &event) {
Ok(_) => {
console_log!("Notified extension about sign request: {}", request.id);
console_log!("✅ WASM: Notified extension about sign request: {}", request.id);
// Return an error to indicate this request should not be auto-signed
// The extension will handle the approval flow
Err(SigSocketError::Other("Request forwarded to extension for approval".to_string()))
}
Err(e) => {
console_log!("Failed to notify extension: {:?}", e);
console_log!("❌ WASM: Failed to notify extension: {:?}", e);
Err(SigSocketError::Other("Extension notification failed".to_string()))
}
}
@@ -72,10 +97,12 @@ pub struct SigSocketManager;
#[wasm_bindgen]
impl SigSocketManager {
/// Connect to SigSocket server with a specific workspace and event callback
/// Connect to SigSocket server with smart connection management
///
/// This establishes a real WebSocket connection using the workspace's default public key
/// and integrates with the vault system for security validation.
/// This handles all connection logic:
/// - Reuses existing connection if same workspace
/// - Switches connection if different workspace
/// - Creates new connection if none exists
///
/// # Arguments
/// * `workspace` - The workspace name to connect with
@@ -98,25 +125,56 @@ impl SigSocketManager {
let public_key_bytes = hex::decode(&public_key_hex)
.map_err(|e| JsValue::from_str(&format!("Invalid public key format: {}", e)))?;
// 3. Create SigSocket client with extension notification handler
let mut client = SigSocketClient::new(server_url, public_key_bytes)
.map_err(|e| JsValue::from_str(&format!("Failed to create client: {:?}", e)))?;
// 3. Check if already connected to same workspace and handle disconnection
let should_connect = SIGSOCKET_CLIENT.with(|c| {
let mut client_opt = c.borrow_mut();
// Set up extension notification handler using existing API
let handler = ExtensionNotificationHandler::new(event_callback.clone());
client.set_sign_handler(handler);
// Check if we already have a client for this workspace
if let Some(existing_client) = client_opt.as_ref() {
if let Some(existing_key) = existing_client.connected_public_key() {
if existing_key == hex::encode(&public_key_bytes) && existing_client.is_connected() {
console_log!("🔄 WASM: Already connected to workspace: {}", workspace);
return false; // Reuse existing connection
} else {
console_log!("🔄 WASM: Switching workspace from {} to {}",
existing_key, hex::encode(&public_key_bytes));
// 4. Connect to the WebSocket server
client.connect().await
.map_err(|e| JsValue::from_str(&format!("Connection failed: {:?}", e)))?;
// Disconnect the old client
*client_opt = None; // This will drop the old client and close WebSocket
console_log!("🔌 WASM: Disconnected from old workspace");
console_log!("SigSocket connected successfully to {}", server_url);
return true; // Need new connection
}
}
}
// 5. Store the connected client
SIGSOCKET_CLIENT.with(|c| {
*c.borrow_mut() = Some(client);
true // Need new connection, no old one to disconnect
});
// 4. Create and connect if needed
if should_connect {
console_log!("🔗 WASM: Creating new connection for workspace: {}", workspace);
// Create new client
let mut client = SigSocketClient::new(server_url, public_key_bytes.clone())
.map_err(|e| JsValue::from_str(&format!("Failed to create client: {:?}", e)))?;
// Set up extension notification handler
let handler = ExtensionNotificationHandler::new(event_callback.clone());
client.set_sign_handler(handler);
// Connect to the WebSocket server
client.connect().await
.map_err(|e| JsValue::from_str(&format!("Connection failed: {:?}", e)))?;
console_log!("✅ WASM: Connected to SigSocket server for workspace: {}", workspace);
// Store the connected client
SIGSOCKET_CLIENT.with(|c| {
*c.borrow_mut() = Some(client);
});
}
// 6. Return connection info
let connection_info = SigSocketConnectionInfo {
workspace: workspace.to_string(),
@@ -150,7 +208,7 @@ impl SigSocketManager {
}
/// Disconnect from SigSocket server
///
///
/// # Returns
/// * `Ok(())` - Successfully disconnected
/// * `Err(error)` - If disconnect failed
@@ -158,9 +216,16 @@ impl SigSocketManager {
pub async fn disconnect() -> Result<(), JsValue> {
SIGSOCKET_CLIENT.with(|c| {
let mut client_opt = c.borrow_mut();
if let Some(_client) = client_opt.take() {
// client.disconnect().await?; // Will be async in real implementation
console_log!("SigSocket client disconnected");
if let Some(client) = client_opt.take() {
let workspace_info = client.connected_public_key()
.map(|key| key[..16].to_string())
.unwrap_or_else(|| "unknown".to_string());
// Dropping the client will close the WebSocket connection
drop(client);
console_log!("🔌 WASM: Disconnected SigSocket client (was: {}...)", workspace_info);
} else {
console_log!("🔌 WASM: No SigSocket client to disconnect");
}
Ok(())
})
@@ -254,27 +319,51 @@ impl SigSocketManager {
// 3. Sign with vault
let signature_result = sign_with_default_keypair(&message_bytes).await?;
let signature_obj: serde_json::Value = serde_json::from_str(&signature_result.as_string().unwrap())
.map_err(|e| JsValue::from_str(&format!("Failed to parse signature: {}", e)))?;
let signature_hex = signature_result.as_string()
.ok_or_else(|| JsValue::from_str("Signature result is not a string"))?;
// Convert hex signature to base64 for SigSocket protocol
let signature_bytes = hex::decode(&signature_hex)
.map_err(|e| JsValue::from_str(&format!("Invalid hex signature: {}", e)))?;
let signature_base64 = base64::prelude::BASE64_STANDARD.encode(&signature_bytes);
let signature_base64 = signature_obj["signature"].as_str()
.ok_or_else(|| JsValue::from_str("Invalid signature format"))?;
// 4. Send response to server and remove request
// 4. Get original message for response
let original_message = SIGSOCKET_CLIENT.with(|c| {
let client = c.borrow();
let client = client.as_ref().ok_or_else(|| JsValue::from_str("Not connected"))?;
let request = client.get_pending_request(request_id)
.ok_or_else(|| JsValue::from_str("Request not found"))?;
Ok::<String, JsValue>(request.request.message.clone())
})?;
// 5. Send response to server (create a new scope to avoid borrowing issues)
{
let client_ref = SIGSOCKET_CLIENT.with(|c| {
c.borrow().as_ref().map(|client| client as *const SigSocketClient)
}).ok_or_else(|| JsValue::from_str("Not connected"))?;
// SAFETY: We know the client exists and we're using it synchronously
let client = unsafe { &*client_ref };
client.send_response(request_id, &original_message, &signature_base64).await
.map_err(|e| JsValue::from_str(&format!("Failed to send response: {:?}", e)))?;
console_log!("✅ WASM: Sent signature response to server for request: {}", request_id);
}
// 6. Remove the request after successful send
SIGSOCKET_CLIENT.with(|c| {
let mut client = c.borrow_mut();
let client = client.as_mut().ok_or_else(|| JsValue::from_str("Not connected"))?;
// Send response (will be async in real implementation)
// client.send_response(request_id, &original_request.message, signature_base64).await?;
// Remove the request
client.remove_pending_request(request_id);
console_log!("Approved and sent signature for request: {}", request_id);
Ok(signature_base64.to_string())
})
if let Some(client) = client.as_mut() {
client.remove_pending_request(request_id);
console_log!("✅ WASM: Removed request from pending list: {}", request_id);
}
});
console_log!("🎉 WASM: Successfully approved and sent signature for request: {}", request_id);
Ok(signature_base64)
}
/// Reject a sign request
@@ -288,20 +377,32 @@ impl SigSocketManager {
/// * `Err(error)` - If rejection failed
#[wasm_bindgen]
pub async fn reject_request(request_id: &str, reason: &str) -> Result<(), JsValue> {
// Send rejection to server first
{
let client_ref = SIGSOCKET_CLIENT.with(|c| {
c.borrow().as_ref().map(|client| client as *const SigSocketClient)
}).ok_or_else(|| JsValue::from_str("Not connected"))?;
// SAFETY: We know the client exists and we're using it synchronously
let client = unsafe { &*client_ref };
client.send_rejection(request_id, reason).await
.map_err(|e| JsValue::from_str(&format!("Failed to send rejection: {:?}", e)))?;
console_log!("✅ WASM: Sent rejection to server for request: {}", request_id);
}
// Remove the request after successful send
SIGSOCKET_CLIENT.with(|c| {
let mut client = c.borrow_mut();
let client = client.as_mut().ok_or_else(|| JsValue::from_str("Not connected"))?;
if let Some(client) = client.as_mut() {
client.remove_pending_request(request_id);
console_log!("✅ WASM: Removed rejected request from pending list: {}", request_id);
}
});
// Send rejection (will be async in real implementation)
// client.send_rejection(request_id, reason).await?;
// Remove the request
client.remove_pending_request(request_id);
console_log!("Rejected request {}: {}", request_id, reason);
Ok(())
})
console_log!("🚫 WASM: Successfully rejected request: {} (reason: {})", request_id, reason);
Ok(())
}
/// Get pending requests filtered by current workspace