Add permission check handlers for extensions

- Add check.rs with Tauri commands for checking web, database, and filesystem permissions
- Implement handlePermissionsMethodAsync in frontend to route permission checks
- Register permission check commands in lib.rs
- Add toast notification for permission denied errors in web requests
- Extensions can now check permissions before operations via SDK
This commit is contained in:
2025-11-11 15:40:01 +01:00
parent 9583e2f44b
commit 2202415441
5 changed files with 200 additions and 5 deletions

View File

@ -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
}

View File

@ -1,3 +1,4 @@
pub mod check;
pub mod manager;
pub mod types;
pub mod validator;

View File

@ -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<HlcService>,
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");

View File

@ -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<string, unknown>,
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<void>('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<string, unknown>,
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<void>('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<string, unknown>,
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<void>('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
}
}

View File

@ -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)}`)
}
}