mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
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:
64
src-tauri/src/extension/permissions/check.rs
Normal file
64
src-tauri/src/extension/permissions/check.rs
Normal 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
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
|
pub mod check;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
pub mod types;
|
pub mod types;
|
||||||
pub mod validator;
|
pub mod validator;
|
||||||
|
|||||||
@ -1,7 +1,14 @@
|
|||||||
mod crdt;
|
mod crdt;
|
||||||
mod database;
|
mod database;
|
||||||
mod extension;
|
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 std::sync::{Arc, Mutex};
|
||||||
use tauri::Manager;
|
use tauri::Manager;
|
||||||
|
|
||||||
@ -9,10 +16,15 @@ pub mod table_names {
|
|||||||
include!(concat!(env!("OUT_DIR"), "/tableNames.rs"));
|
include!(concat!(env!("OUT_DIR"), "/tableNames.rs"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod event_names {
|
||||||
|
include!(concat!(env!("OUT_DIR"), "/eventNames.rs"));
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
pub db: DbConnection,
|
pub db: DbConnection,
|
||||||
pub hlc: Mutex<HlcService>,
|
pub hlc: Mutex<HlcService>,
|
||||||
pub extension_manager: ExtensionManager,
|
pub extension_manager: ExtensionManager,
|
||||||
|
pub extension_webview_manager: ExtensionWebviewManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||||
@ -54,6 +66,7 @@ pub fn run() {
|
|||||||
db: DbConnection(Arc::new(Mutex::new(None))),
|
db: DbConnection(Arc::new(Mutex::new(None))),
|
||||||
hlc: Mutex::new(HlcService::new()),
|
hlc: Mutex::new(HlcService::new()),
|
||||||
extension_manager: ExtensionManager::new(),
|
extension_manager: ExtensionManager::new(),
|
||||||
|
extension_webview_manager: ExtensionWebviewManager::new(),
|
||||||
})
|
})
|
||||||
//.manage(ExtensionState::default())
|
//.manage(ExtensionState::default())
|
||||||
.plugin(tauri_plugin_dialog::init())
|
.plugin(tauri_plugin_dialog::init())
|
||||||
@ -80,6 +93,9 @@ pub fn run() {
|
|||||||
extension::database::extension_sql_select,
|
extension::database::extension_sql_select,
|
||||||
extension::web::extension_web_fetch,
|
extension::web::extension_web_fetch,
|
||||||
extension::web::extension_web_open,
|
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_dev_extensions,
|
||||||
extension::get_all_extensions,
|
extension::get_all_extensions,
|
||||||
extension::get_extension_info,
|
extension::get_extension_info,
|
||||||
@ -89,6 +105,11 @@ pub fn run() {
|
|||||||
extension::preview_extension,
|
extension::preview_extension,
|
||||||
extension::remove_dev_extension,
|
extension::remove_dev_extension,
|
||||||
extension::remove_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!())
|
.run(tauri::generate_context!())
|
||||||
.expect("error while running tauri application");
|
.expect("error while running tauri application");
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
import type { IHaexHubExtension } from '~/types/haexhub'
|
import type { IHaexHubExtension } from '~/types/haexhub'
|
||||||
import type { ExtensionRequest } from './types'
|
import type { ExtensionRequest } from './types'
|
||||||
|
import { invoke } from '@tauri-apps/api/core'
|
||||||
|
|
||||||
export async function handlePermissionsMethodAsync(
|
export async function handlePermissionsMethodAsync(
|
||||||
request: ExtensionRequest,
|
request: ExtensionRequest,
|
||||||
@ -9,6 +10,102 @@ export async function handlePermissionsMethodAsync(
|
|||||||
throw new Error('Extension not found')
|
throw new Error('Extension not found')
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Implementiere Permission Request UI
|
const { method, params } = request
|
||||||
throw new Error('Permission methods not yet implemented')
|
|
||||||
|
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
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -62,11 +62,23 @@ async function handleWebFetchAsync(
|
|||||||
body: response.body,
|
body: response.body,
|
||||||
url: response.url,
|
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) {
|
if (error instanceof Error) {
|
||||||
throw new Error(`Web request failed: ${error.message}`)
|
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)}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user