mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 14:10:52 +01:00
Rename Http to Web and implement permission checks
- Rename ResourceType::Http to ResourceType::Web - Rename HttpAction to WebAction - Rename HttpConstraints to WebConstraints - Rename Action::Http to Action::Web - Add check_web_permission method to PermissionManager - Optimize permission loading (only fetch web permissions) - Add permission checks to extension_web_fetch and extension_web_open - Update manifest.rs to use Web instead of Http
This commit is contained in:
@ -1,6 +1,6 @@
|
||||
use crate::extension::error::ExtensionError;
|
||||
use crate::extension::permissions::types::{
|
||||
Action, DbAction, ExtensionPermission, FsAction, HttpAction, PermissionConstraints,
|
||||
Action, DbAction, ExtensionPermission, FsAction, WebAction, PermissionConstraints,
|
||||
PermissionStatus, ResourceType, ShellAction,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -117,7 +117,7 @@ impl ExtensionPermissions {
|
||||
}
|
||||
if let Some(entries) = &self.http {
|
||||
for p in entries {
|
||||
if let Some(perm) = Self::create_internal(extension_id, ResourceType::Http, p) {
|
||||
if let Some(perm) = Self::create_internal(extension_id, ResourceType::Web, p) {
|
||||
permissions.push(perm);
|
||||
}
|
||||
}
|
||||
@ -146,7 +146,7 @@ impl ExtensionPermissions {
|
||||
ResourceType::Fs => FsAction::from_str(operation_str)
|
||||
.ok()
|
||||
.map(Action::Filesystem),
|
||||
ResourceType::Http => HttpAction::from_str(operation_str).ok().map(Action::Http),
|
||||
ResourceType::Web => WebAction::from_str(operation_str).ok().map(Action::Web),
|
||||
ResourceType::Shell => ShellAction::from_str(operation_str).ok().map(Action::Shell),
|
||||
};
|
||||
|
||||
|
||||
@ -4,7 +4,7 @@ use crate::database::core::with_connection;
|
||||
use crate::database::error::DatabaseError;
|
||||
use crate::extension::database::executor::SqlExecutor;
|
||||
use crate::extension::error::ExtensionError;
|
||||
use crate::extension::permissions::types::{Action, ExtensionPermission, PermissionStatus, ResourceType};
|
||||
use crate::extension::permissions::types::{Action, ExtensionPermission, PermissionConstraints, PermissionStatus, ResourceType};
|
||||
use tauri::State;
|
||||
use crate::database::generated::HaexExtensionPermissions;
|
||||
use rusqlite::params;
|
||||
@ -245,6 +245,74 @@ impl PermissionManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prüft Web-Berechtigungen für Requests
|
||||
pub async fn check_web_permission(
|
||||
app_state: &State<'_, AppState>,
|
||||
extension_id: &str,
|
||||
method: &str,
|
||||
url: &str,
|
||||
) -> Result<(), ExtensionError> {
|
||||
// Optimiert: Lade nur Web-Permissions aus der Datenbank
|
||||
let permissions = with_connection(&app_state.db, |conn| {
|
||||
let sql = format!(
|
||||
"SELECT * FROM {TABLE_EXTENSION_PERMISSIONS} WHERE extension_id = ? AND resource_type = 'web'"
|
||||
);
|
||||
let mut stmt = conn.prepare(&sql).map_err(DatabaseError::from)?;
|
||||
|
||||
let perms_iter = stmt.query_map(params![extension_id], |row| {
|
||||
crate::database::generated::HaexExtensionPermissions::from_row(row)
|
||||
})?;
|
||||
|
||||
let permissions: Vec<ExtensionPermission> = perms_iter
|
||||
.filter_map(Result::ok)
|
||||
.map(Into::into)
|
||||
.collect();
|
||||
|
||||
Ok(permissions)
|
||||
})?;
|
||||
|
||||
let url_parsed = url::Url::parse(url).map_err(|e| ExtensionError::ValidationError {
|
||||
reason: format!("Invalid URL: {}", e),
|
||||
})?;
|
||||
|
||||
let domain = url_parsed.host_str().ok_or_else(|| ExtensionError::ValidationError {
|
||||
reason: "URL does not contain a valid host".to_string(),
|
||||
})?;
|
||||
|
||||
let has_permission = permissions
|
||||
.iter()
|
||||
.filter(|perm| perm.status == PermissionStatus::Granted)
|
||||
.any(|perm| {
|
||||
let domain_matches = perm.target == "*"
|
||||
|| perm.target == domain
|
||||
|| domain.ends_with(&format!(".{}", perm.target));
|
||||
|
||||
if !domain_matches {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(PermissionConstraints::Web(constraints)) = &perm.constraints {
|
||||
if let Some(methods) = &constraints.methods {
|
||||
if !methods.iter().any(|m| m.eq_ignore_ascii_case(method)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
if !has_permission {
|
||||
return Err(ExtensionError::permission_denied(
|
||||
extension_id,
|
||||
method,
|
||||
&format!("web request to '{}'", url),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/* /// Prüft Dateisystem-Berechtigungen
|
||||
pub async fn check_filesystem_permission(
|
||||
app_state: &State<'_, AppState>,
|
||||
@ -293,56 +361,6 @@ impl PermissionManager {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prüft HTTP-Berechtigungen
|
||||
pub async fn check_http_permission(
|
||||
app_state: &State<'_, AppState>,
|
||||
extension_id: &str,
|
||||
method: &str,
|
||||
url: &str,
|
||||
) -> Result<(), ExtensionError> {
|
||||
let permissions = Self::get_permissions(app_state, extension_id).await?;
|
||||
|
||||
let url_parsed = Url::parse(url).map_err(|e| ExtensionError::ValidationError {
|
||||
reason: format!("Invalid URL: {}", e),
|
||||
})?;
|
||||
|
||||
let domain = url_parsed.host_str().unwrap_or("");
|
||||
|
||||
let has_permission = permissions
|
||||
.iter()
|
||||
.filter(|perm| perm.status == PermissionStatus::Granted)
|
||||
.filter(|perm| perm.resource_type == ResourceType::Http)
|
||||
.any(|perm| {
|
||||
let domain_matches = perm.target == "*"
|
||||
|| perm.target == domain
|
||||
|| domain.ends_with(&format!(".{}", perm.target));
|
||||
|
||||
if !domain_matches {
|
||||
return false;
|
||||
}
|
||||
|
||||
if let Some(PermissionConstraints::Http(constraints)) = &perm.constraints {
|
||||
if let Some(methods) = &constraints.methods {
|
||||
if !methods.iter().any(|m| m.eq_ignore_ascii_case(method)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
});
|
||||
|
||||
if !has_permission {
|
||||
return Err(ExtensionError::permission_denied(
|
||||
extension_id,
|
||||
method,
|
||||
&format!("HTTP request to '{}'", url),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Prüft Shell-Berechtigungen
|
||||
pub async fn check_shell_permission(
|
||||
app_state: &State<'_, AppState>,
|
||||
@ -410,7 +428,7 @@ impl PermissionManager {
|
||||
pub fn parse_resource_type(s: &str) -> Result<ResourceType, DatabaseError> {
|
||||
match s {
|
||||
"fs" => Ok(ResourceType::Fs),
|
||||
"http" => Ok(ResourceType::Http),
|
||||
"web" => Ok(ResourceType::Web),
|
||||
"db" => Ok(ResourceType::Db),
|
||||
"shell" => Ok(ResourceType::Shell),
|
||||
_ => Err(DatabaseError::SerializationError {
|
||||
|
||||
@ -86,11 +86,11 @@ impl FromStr for FsAction {
|
||||
}
|
||||
}
|
||||
|
||||
/// Definiert Aktionen (HTTP-Methoden), die auf HTTP-Anfragen angewendet werden können.
|
||||
/// Definiert Aktionen (HTTP-Methoden), die auf Web-Anfragen angewendet werden können.
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, TS)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
#[ts(export)]
|
||||
pub enum HttpAction {
|
||||
pub enum WebAction {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
@ -100,20 +100,20 @@ pub enum HttpAction {
|
||||
All,
|
||||
}
|
||||
|
||||
impl FromStr for HttpAction {
|
||||
impl FromStr for WebAction {
|
||||
type Err = ExtensionError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_uppercase().as_str() {
|
||||
"GET" => Ok(HttpAction::Get),
|
||||
"POST" => Ok(HttpAction::Post),
|
||||
"PUT" => Ok(HttpAction::Put),
|
||||
"PATCH" => Ok(HttpAction::Patch),
|
||||
"DELETE" => Ok(HttpAction::Delete),
|
||||
"*" => Ok(HttpAction::All),
|
||||
"GET" => Ok(WebAction::Get),
|
||||
"POST" => Ok(WebAction::Post),
|
||||
"PUT" => Ok(WebAction::Put),
|
||||
"PATCH" => Ok(WebAction::Patch),
|
||||
"DELETE" => Ok(WebAction::Delete),
|
||||
"*" => Ok(WebAction::All),
|
||||
_ => Err(ExtensionError::InvalidActionString {
|
||||
input: s.to_string(),
|
||||
resource_type: "http".to_string(),
|
||||
resource_type: "web".to_string(),
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -149,7 +149,7 @@ impl FromStr for ShellAction {
|
||||
pub enum Action {
|
||||
Database(DbAction),
|
||||
Filesystem(FsAction),
|
||||
Http(HttpAction),
|
||||
Web(WebAction),
|
||||
Shell(ShellAction),
|
||||
}
|
||||
|
||||
@ -173,7 +173,7 @@ pub struct ExtensionPermission {
|
||||
#[ts(export)]
|
||||
pub enum ResourceType {
|
||||
Fs,
|
||||
Http,
|
||||
Web,
|
||||
Db,
|
||||
Shell,
|
||||
}
|
||||
@ -195,7 +195,7 @@ pub enum PermissionStatus {
|
||||
pub enum PermissionConstraints {
|
||||
Database(DbConstraints),
|
||||
Filesystem(FsConstraints),
|
||||
Http(HttpConstraints),
|
||||
Web(WebConstraints),
|
||||
Shell(ShellConstraints),
|
||||
}
|
||||
|
||||
@ -223,7 +223,7 @@ pub struct FsConstraints {
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, TS)]
|
||||
#[ts(export)]
|
||||
pub struct HttpConstraints {
|
||||
pub struct WebConstraints {
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub methods: Option<Vec<String>>,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@ -254,7 +254,7 @@ impl ResourceType {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
ResourceType::Fs => "fs",
|
||||
ResourceType::Http => "http",
|
||||
ResourceType::Web => "web",
|
||||
ResourceType::Db => "db",
|
||||
ResourceType::Shell => "shell",
|
||||
}
|
||||
@ -263,7 +263,7 @@ impl ResourceType {
|
||||
pub fn from_str(s: &str) -> Result<Self, ExtensionError> {
|
||||
match s {
|
||||
"fs" => Ok(ResourceType::Fs),
|
||||
"http" => Ok(ResourceType::Http),
|
||||
"web" => Ok(ResourceType::Web),
|
||||
"db" => Ok(ResourceType::Db),
|
||||
"shell" => Ok(ResourceType::Shell),
|
||||
_ => Err(ExtensionError::ValidationError {
|
||||
@ -284,7 +284,7 @@ impl Action {
|
||||
.unwrap_or_default()
|
||||
.trim_matches('"')
|
||||
.to_string(),
|
||||
Action::Http(action) => serde_json::to_string(action)
|
||||
Action::Web(action) => serde_json::to_string(action)
|
||||
.unwrap_or_default()
|
||||
.trim_matches('"')
|
||||
.to_string(),
|
||||
@ -299,15 +299,15 @@ impl Action {
|
||||
match resource_type {
|
||||
ResourceType::Db => Ok(Action::Database(DbAction::from_str(s)?)),
|
||||
ResourceType::Fs => Ok(Action::Filesystem(FsAction::from_str(s)?)),
|
||||
ResourceType::Http => {
|
||||
let action: HttpAction =
|
||||
ResourceType::Web => {
|
||||
let action: WebAction =
|
||||
serde_json::from_str(&format!("\"{s}\"")).map_err(|_| {
|
||||
ExtensionError::InvalidActionString {
|
||||
input: s.to_string(),
|
||||
resource_type: "http".to_string(),
|
||||
resource_type: "web".to_string(),
|
||||
}
|
||||
})?;
|
||||
Ok(Action::Http(action))
|
||||
Ok(Action::Web(action))
|
||||
}
|
||||
ResourceType::Shell => Ok(Action::Shell(ShellAction::from_str(s)?)),
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ pub async fn extension_web_open(
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<(), ExtensionError> {
|
||||
// Get extension to validate it exists
|
||||
let _extension = state
|
||||
let extension = state
|
||||
.extension_manager
|
||||
.get_extension_by_public_key_and_name(&public_key, &name)?
|
||||
.ok_or_else(|| ExtensionError::NotFound {
|
||||
@ -62,6 +62,15 @@ pub async fn extension_web_open(
|
||||
});
|
||||
}
|
||||
|
||||
// Check web permissions (open uses GET method for permission check)
|
||||
crate::extension::permissions::manager::PermissionManager::check_web_permission(
|
||||
&state,
|
||||
&extension.id,
|
||||
"GET",
|
||||
&url,
|
||||
)
|
||||
.await?;
|
||||
|
||||
// Open URL in default browser using tauri-plugin-opener
|
||||
tauri_plugin_opener::open_url(&url, None::<&str>).map_err(|e| ExtensionError::WebError {
|
||||
reason: format!("Failed to open URL in browser: {}", e),
|
||||
@ -82,7 +91,7 @@ pub async fn extension_web_fetch(
|
||||
state: State<'_, AppState>,
|
||||
) -> Result<WebFetchResponse, ExtensionError> {
|
||||
// Get extension to validate it exists
|
||||
let _extension = state
|
||||
let extension = state
|
||||
.extension_manager
|
||||
.get_extension_by_public_key_and_name(&public_key, &name)?
|
||||
.ok_or_else(|| ExtensionError::NotFound {
|
||||
@ -90,13 +99,20 @@ pub async fn extension_web_fetch(
|
||||
name: name.clone(),
|
||||
})?;
|
||||
|
||||
// TODO: Add permission check for web requests once permission system is complete
|
||||
// For now, extensions are allowed to make web requests
|
||||
// Use _extension for permission validation when implemented
|
||||
let method_str = method.as_deref().unwrap_or("GET");
|
||||
|
||||
// Check web permissions before making request
|
||||
crate::extension::permissions::manager::PermissionManager::check_web_permission(
|
||||
&state,
|
||||
&extension.id,
|
||||
method_str,
|
||||
&url,
|
||||
)
|
||||
.await?;
|
||||
|
||||
let request = WebFetchRequest {
|
||||
url,
|
||||
method,
|
||||
method: Some(method_str.to_string()),
|
||||
headers,
|
||||
body,
|
||||
timeout,
|
||||
|
||||
Reference in New Issue
Block a user