diff --git a/src-tauri/src/extension/permissions/check.rs b/src-tauri/src/extension/permissions/check.rs new file mode 100644 index 0000000..afaca06 --- /dev/null +++ b/src-tauri/src/extension/permissions/check.rs @@ -0,0 +1,64 @@ +// src-tauri/src/extension/permissions/commands.rs + +use crate::extension::error::ExtensionError; +use crate::extension::permissions::manager::PermissionManager; +use crate::AppState; +use tauri::State; + +#[tauri::command] +pub async fn check_web_permission( + extension_id: String, + method: String, + url: String, + state: State<'_, AppState>, +) -> Result<(), ExtensionError> { + PermissionManager::check_web_permission(&state, &extension_id, &method, &url).await +} + +#[tauri::command] +pub async fn check_database_permission( + extension_id: String, + resource: String, + operation: String, + state: State<'_, AppState>, +) -> Result<(), ExtensionError> { + let action = match operation.as_str() { + "read" => crate::extension::permissions::types::Action::Database( + crate::extension::permissions::types::DbAction::Read, + ), + "write" => crate::extension::permissions::types::Action::Database( + crate::extension::permissions::types::DbAction::ReadWrite, + ), + _ => { + return Err(ExtensionError::ValidationError { + reason: format!("Invalid database operation: {}", operation), + }) + } + }; + + PermissionManager::check_database_permission(&state, &extension_id, action, &resource).await +} + +#[tauri::command] +pub async fn check_filesystem_permission( + extension_id: String, + path: String, + operation: String, + state: State<'_, AppState>, +) -> Result<(), ExtensionError> { + let action = match operation.as_str() { + "read" => crate::extension::permissions::types::Action::Filesystem( + crate::extension::permissions::types::FsAction::Read, + ), + "write" => crate::extension::permissions::types::Action::Filesystem( + crate::extension::permissions::types::FsAction::ReadWrite, + ), + _ => { + return Err(ExtensionError::ValidationError { + reason: format!("Invalid filesystem operation: {}", operation), + }) + } + }; + + PermissionManager::check_filesystem_permission(&state, &extension_id, action, &path).await +} diff --git a/src-tauri/src/extension/permissions/mod.rs b/src-tauri/src/extension/permissions/mod.rs index 740af61..b49306a 100644 --- a/src-tauri/src/extension/permissions/mod.rs +++ b/src-tauri/src/extension/permissions/mod.rs @@ -1,3 +1,4 @@ +pub mod check; pub mod manager; pub mod types; pub mod validator; diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 6002597..1e0399e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,7 +1,14 @@ mod crdt; mod database; mod extension; -use crate::{crdt::hlc::HlcService, database::DbConnection, extension::core::ExtensionManager}; +use crate::{ + crdt::hlc::HlcService, + database::DbConnection, + extension::{ + core::ExtensionManager, + webview::ExtensionWebviewManager, + } +}; use std::sync::{Arc, Mutex}; use tauri::Manager; @@ -9,10 +16,15 @@ pub mod table_names { include!(concat!(env!("OUT_DIR"), "/tableNames.rs")); } +pub mod event_names { + include!(concat!(env!("OUT_DIR"), "/eventNames.rs")); +} + pub struct AppState { pub db: DbConnection, pub hlc: Mutex, pub extension_manager: ExtensionManager, + pub extension_webview_manager: ExtensionWebviewManager, } #[cfg_attr(mobile, tauri::mobile_entry_point)] @@ -54,6 +66,7 @@ pub fn run() { db: DbConnection(Arc::new(Mutex::new(None))), hlc: Mutex::new(HlcService::new()), extension_manager: ExtensionManager::new(), + extension_webview_manager: ExtensionWebviewManager::new(), }) //.manage(ExtensionState::default()) .plugin(tauri_plugin_dialog::init()) @@ -80,6 +93,9 @@ pub fn run() { extension::database::extension_sql_select, extension::web::extension_web_fetch, extension::web::extension_web_open, + extension::permissions::check::check_web_permission, + extension::permissions::check::check_database_permission, + extension::permissions::check::check_filesystem_permission, extension::get_all_dev_extensions, extension::get_all_extensions, extension::get_extension_info, @@ -89,6 +105,11 @@ pub fn run() { extension::preview_extension, extension::remove_dev_extension, extension::remove_extension, + extension::open_extension_webview_window, + extension::close_extension_webview_window, + extension::focus_extension_webview_window, + extension::update_extension_webview_window_position, + extension::update_extension_webview_window_size, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); diff --git a/src/composables/handlers/permissions.ts b/src/composables/handlers/permissions.ts index ec68c4b..bdecf1c 100644 --- a/src/composables/handlers/permissions.ts +++ b/src/composables/handlers/permissions.ts @@ -1,5 +1,6 @@ import type { IHaexHubExtension } from '~/types/haexhub' import type { ExtensionRequest } from './types' +import { invoke } from '@tauri-apps/api/core' export async function handlePermissionsMethodAsync( request: ExtensionRequest, @@ -9,6 +10,102 @@ export async function handlePermissionsMethodAsync( throw new Error('Extension not found') } - // TODO: Implementiere Permission Request UI - throw new Error('Permission methods not yet implemented') + const { method, params } = request + + if (method === 'permissions.web.check') { + return await checkWebPermissionAsync(params, extension) + } + + if (method === 'permissions.database.check') { + return await checkDatabasePermissionAsync(params, extension) + } + + if (method === 'permissions.filesystem.check') { + return await checkFilesystemPermissionAsync(params, extension) + } + + throw new Error(`Unknown permission method: ${method}`) +} + +async function checkWebPermissionAsync( + params: Record, + extension: IHaexHubExtension, +) { + const url = params.url as string + const method = (params.method as string) || 'GET' + + if (!url) { + throw new Error('URL is required') + } + + try { + await invoke('check_web_permission', { + extensionId: extension.id, + method, + url, + }) + + return { status: 'granted' } + } catch (error: any) { + // Permission denied errors return a specific error code + if (error?.code === 1002 || error?.message?.includes('Permission denied')) { + return { status: 'denied' } + } + // Other errors should be thrown + throw error + } +} + +async function checkDatabasePermissionAsync( + params: Record, + extension: IHaexHubExtension, +) { + const resource = params.resource as string + const operation = params.operation as string + + if (!resource || !operation) { + throw new Error('Resource and operation are required') + } + + try { + await invoke('check_database_permission', { + extensionId: extension.id, + resource, + operation, + }) + + return { status: 'granted' } + } catch (error: any) { + if (error?.code === 1002 || error?.message?.includes('Permission denied')) { + return { status: 'denied' } + } + throw error + } +} + +async function checkFilesystemPermissionAsync( + params: Record, + extension: IHaexHubExtension, +) { + const path = params.path as string + const operation = params.operation as string + + if (!path || !operation) { + throw new Error('Path and operation are required') + } + + try { + await invoke('check_filesystem_permission', { + extensionId: extension.id, + path, + operation, + }) + + return { status: 'granted' } + } catch (error: any) { + if (error?.code === 1002 || error?.message?.includes('Permission denied')) { + return { status: 'denied' } + } + throw error + } } diff --git a/src/composables/handlers/web.ts b/src/composables/handlers/web.ts index e6d9ea8..83c76e8 100644 --- a/src/composables/handlers/web.ts +++ b/src/composables/handlers/web.ts @@ -62,11 +62,23 @@ async function handleWebFetchAsync( body: response.body, url: response.url, } - } catch (error) { + } catch (error: any) { + console.error('Web request error:', error) + + // Check if it's a permission denied error + if (error?.code === 1002 || error?.message?.includes('Permission denied')) { + const toast = useToast() + toast.add({ + title: 'Permission denied', + description: `Extension "${extension.name}" does not have permission to access ${url}`, + color: 'error', + }) + } + if (error instanceof Error) { throw new Error(`Web request failed: ${error.message}`) } - throw new Error('Web request failed with unknown error') + throw new Error(`Web request failed with unknown error: ${JSON.stringify(error)}`) } }