mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-18 15:00:52 +01:00
refactored install dialog
This commit is contained in:
@ -1,14 +1,60 @@
|
||||
// src-tauri/src/extension/core/manifest.rs
|
||||
|
||||
use crate::extension::crypto::ExtensionCrypto;
|
||||
use crate::extension::error::ExtensionError;
|
||||
use crate::extension::permissions::types::{
|
||||
Action, DbConstraints, ExtensionPermission, FsConstraints, HttpConstraints,
|
||||
PermissionConstraints, PermissionStatus, ResourceType, ShellConstraints,
|
||||
Action, DbAction, ExtensionPermission, FsAction, HttpAction, PermissionConstraints,
|
||||
PermissionStatus, ResourceType, ShellAction,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
use ts_rs::TS;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
/// Repräsentiert einen einzelnen Berechtigungseintrag im Manifest und im UI-Modell.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, TS)]
|
||||
#[ts(export)]
|
||||
pub struct PermissionEntry {
|
||||
pub target: String,
|
||||
|
||||
/// Die auszuführende Aktion (z.B. "read", "read_write", "GET", "execute").
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub operation: Option<String>,
|
||||
|
||||
/// Optionale, spezifische Einschränkungen für diese Berechtigung.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
#[ts(type = "Record<string, unknown>")]
|
||||
pub constraints: Option<serde_json::Value>,
|
||||
|
||||
/// Der Status der Berechtigung (wird nur im UI-Modell verwendet).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub status: Option<PermissionStatus>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, TS)]
|
||||
#[ts(export)]
|
||||
pub struct ExtensionPreview {
|
||||
pub manifest: ExtensionManifest,
|
||||
pub is_valid_signature: bool,
|
||||
pub key_hash: String,
|
||||
pub editable_permissions: EditablePermissions,
|
||||
}
|
||||
/// Definiert die einheitliche Struktur für alle Berechtigungsarten im Manifest und UI.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default, TS)]
|
||||
#[ts(export)]
|
||||
pub struct ExtensionPermissions {
|
||||
#[serde(default)]
|
||||
pub database: Option<Vec<PermissionEntry>>,
|
||||
#[serde(default)]
|
||||
pub filesystem: Option<Vec<PermissionEntry>>,
|
||||
#[serde(default)]
|
||||
pub http: Option<Vec<PermissionEntry>>,
|
||||
#[serde(default)]
|
||||
pub shell: Option<Vec<PermissionEntry>>,
|
||||
}
|
||||
|
||||
/// Typ-Alias für bessere Lesbarkeit, wenn die Struktur als UI-Modell verwendet wird.
|
||||
pub type EditablePermissions = ExtensionPermissions;
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[ts(export)]
|
||||
pub struct ExtensionManifest {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
@ -18,7 +64,7 @@ pub struct ExtensionManifest {
|
||||
pub icon: Option<String>,
|
||||
pub public_key: String,
|
||||
pub signature: String,
|
||||
pub permissions: ExtensionManifestPermissions,
|
||||
pub permissions: ExtensionPermissions,
|
||||
pub homepage: Option<String>,
|
||||
pub description: Option<String>,
|
||||
}
|
||||
@ -31,192 +77,104 @@ impl ExtensionManifest {
|
||||
|
||||
pub fn full_extension_id(&self) -> Result<String, ExtensionError> {
|
||||
let key_hash = self.calculate_key_hash()?;
|
||||
Ok(format!("{}-{}", key_hash, self.id))
|
||||
Ok(format!("{}_{}_{}", key_hash, self.name, self.version))
|
||||
}
|
||||
|
||||
/// Konvertiert die Manifest-Berechtigungen in das bearbeitbare UI-Modell,
|
||||
/// indem der Standardstatus `Granted` gesetzt wird.
|
||||
pub fn to_editable_permissions(&self) -> EditablePermissions {
|
||||
let mut editable = self.permissions.clone();
|
||||
|
||||
let set_status_for_list = |list: Option<&mut Vec<PermissionEntry>>| {
|
||||
if let Some(entries) = list {
|
||||
for entry in entries.iter_mut() {
|
||||
entry.status = Some(PermissionStatus::Granted);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
set_status_for_list(editable.database.as_mut());
|
||||
set_status_for_list(editable.filesystem.as_mut());
|
||||
set_status_for_list(editable.http.as_mut());
|
||||
set_status_for_list(editable.shell.as_mut());
|
||||
|
||||
editable
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtensionPermissions {
|
||||
/// Konvertiert das UI-Modell in die flache Liste von internen `ExtensionPermission`-Objekten.
|
||||
pub fn to_internal_permissions(&self, extension_id: &str) -> Vec<ExtensionPermission> {
|
||||
let mut permissions = Vec::new();
|
||||
|
||||
if let Some(db) = &self.permissions.database {
|
||||
for resource in &db.read {
|
||||
permissions.push(EditablePermission {
|
||||
resource_type: "db".to_string(),
|
||||
action: "read".to_string(),
|
||||
target: resource.clone(),
|
||||
constraints: None,
|
||||
status: "granted".to_string(),
|
||||
});
|
||||
if let Some(entries) = &self.database {
|
||||
for p in entries {
|
||||
if let Some(perm) = Self::create_internal(extension_id, ResourceType::Db, p) {
|
||||
permissions.push(perm);
|
||||
}
|
||||
}
|
||||
for resource in &db.write {
|
||||
permissions.push(EditablePermission {
|
||||
resource_type: "db".to_string(),
|
||||
action: "write".to_string(),
|
||||
target: resource.clone(),
|
||||
constraints: None,
|
||||
status: "granted".to_string(),
|
||||
});
|
||||
}
|
||||
if let Some(entries) = &self.filesystem {
|
||||
for p in entries {
|
||||
if let Some(perm) = Self::create_internal(extension_id, ResourceType::Fs, p) {
|
||||
permissions.push(perm);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(entries) = &self.http {
|
||||
for p in entries {
|
||||
if let Some(perm) = Self::create_internal(extension_id, ResourceType::Http, p) {
|
||||
permissions.push(perm);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(entries) = &self.shell {
|
||||
for p in entries {
|
||||
if let Some(perm) = Self::create_internal(extension_id, ResourceType::Shell, p) {
|
||||
permissions.push(perm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(fs) = &self.permissions.filesystem {
|
||||
for path in &fs.read {
|
||||
permissions.push(EditablePermission {
|
||||
resource_type: "fs".to_string(),
|
||||
action: "read".to_string(),
|
||||
target: path.clone(),
|
||||
constraints: None,
|
||||
status: "granted".to_string(),
|
||||
});
|
||||
}
|
||||
for path in &fs.write {
|
||||
permissions.push(EditablePermission {
|
||||
resource_type: "fs".to_string(),
|
||||
action: "write".to_string(),
|
||||
target: path.clone(),
|
||||
constraints: None,
|
||||
status: "granted".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
permissions
|
||||
}
|
||||
|
||||
if let Some(http_list) = &self.permissions.http {
|
||||
for domain in http_list {
|
||||
permissions.push(EditablePermission {
|
||||
resource_type: "http".to_string(),
|
||||
action: "read".to_string(),
|
||||
target: domain.clone(),
|
||||
constraints: None,
|
||||
status: "granted".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
/// Parst einen einzelnen `PermissionEntry` und wandelt ihn in die interne, typsichere `ExtensionPermission`-Struktur um.
|
||||
fn create_internal(
|
||||
extension_id: &str,
|
||||
resource_type: ResourceType,
|
||||
p: &PermissionEntry,
|
||||
) -> Option<ExtensionPermission> {
|
||||
let operation_str = p.operation.as_deref().unwrap_or_default();
|
||||
|
||||
if let Some(shell_list) = &self.permissions.shell {
|
||||
for command in shell_list {
|
||||
permissions.push(EditablePermission {
|
||||
resource_type: "shell".to_string(),
|
||||
action: "read".to_string(),
|
||||
target: command.clone(),
|
||||
constraints: None,
|
||||
status: "granted".to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
let action = match resource_type {
|
||||
ResourceType::Db => DbAction::from_str(operation_str).ok().map(Action::Database),
|
||||
ResourceType::Fs => FsAction::from_str(operation_str)
|
||||
.ok()
|
||||
.map(Action::Filesystem),
|
||||
ResourceType::Http => HttpAction::from_str(operation_str).ok().map(Action::Http),
|
||||
ResourceType::Shell => ShellAction::from_str(operation_str).ok().map(Action::Shell),
|
||||
};
|
||||
|
||||
EditablePermissions { permissions }
|
||||
action.map(|act| ExtensionPermission {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
extension_id: extension_id.to_string(),
|
||||
resource_type: resource_type.clone(),
|
||||
action: act,
|
||||
target: p.target.clone(),
|
||||
constraints: p
|
||||
.constraints
|
||||
.as_ref()
|
||||
.and_then(|c| serde_json::from_value::<PermissionConstraints>(c.clone()).ok()),
|
||||
status: p.status.clone().unwrap_or(PermissionStatus::Ask),
|
||||
haex_timestamp: None,
|
||||
haex_tombstone: None,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct ExtensionManifestPermissions {
|
||||
#[serde(default)]
|
||||
pub database: Option<DatabaseManifestPermissions>,
|
||||
#[serde(default)]
|
||||
pub filesystem: Option<FilesystemManifestPermissions>,
|
||||
#[serde(default)]
|
||||
pub http: Option<Vec<String>>,
|
||||
#[serde(default)]
|
||||
pub shell: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct DatabaseManifestPermissions {
|
||||
#[serde(default)]
|
||||
pub read: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub write: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct FilesystemManifestPermissions {
|
||||
#[serde(default)]
|
||||
pub read: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub write: Vec<String>,
|
||||
}
|
||||
|
||||
// Editable Permissions für UI
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct EditablePermissions {
|
||||
pub permissions: Vec<EditablePermission>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct EditablePermission {
|
||||
pub resource_type: String,
|
||||
pub action: String,
|
||||
pub target: String,
|
||||
pub constraints: Option<serde_json::Value>,
|
||||
pub status: String,
|
||||
}
|
||||
|
||||
impl EditablePermissions {
|
||||
pub fn to_internal_permissions(&self, extension_id: &str) -> Vec<ExtensionPermission> {
|
||||
self.permissions
|
||||
.iter()
|
||||
.map(|p| ExtensionPermission {
|
||||
id: uuid::Uuid::new_v4().to_string(),
|
||||
extension_id: extension_id.to_string(),
|
||||
resource_type: match p.resource_type.as_str() {
|
||||
"fs" => ResourceType::Fs,
|
||||
"http" => ResourceType::Http,
|
||||
"db" => ResourceType::Db,
|
||||
"shell" => ResourceType::Shell,
|
||||
_ => ResourceType::Fs,
|
||||
},
|
||||
action: match p.action.as_str() {
|
||||
"read" => Action::Read,
|
||||
"write" => Action::Write,
|
||||
_ => Action::Read,
|
||||
},
|
||||
target: p.target.clone(),
|
||||
constraints: p
|
||||
.constraints
|
||||
.as_ref()
|
||||
.and_then(|c| Self::parse_constraints(&p.resource_type, c)),
|
||||
status: match p.status.as_str() {
|
||||
"granted" => PermissionStatus::Granted,
|
||||
"denied" => PermissionStatus::Denied,
|
||||
"ask" => PermissionStatus::Ask,
|
||||
_ => PermissionStatus::Denied,
|
||||
},
|
||||
haex_timestamp: None,
|
||||
haex_tombstone: None,
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn parse_constraints(
|
||||
resource_type: &str,
|
||||
json_value: &serde_json::Value,
|
||||
) -> Option<PermissionConstraints> {
|
||||
match resource_type {
|
||||
"db" => serde_json::from_value::<DbConstraints>(json_value.clone())
|
||||
.ok()
|
||||
.map(PermissionConstraints::Database),
|
||||
"fs" => serde_json::from_value::<FsConstraints>(json_value.clone())
|
||||
.ok()
|
||||
.map(PermissionConstraints::Filesystem),
|
||||
"http" => serde_json::from_value::<HttpConstraints>(json_value.clone())
|
||||
.ok()
|
||||
.map(PermissionConstraints::Http),
|
||||
"shell" => serde_json::from_value::<ShellConstraints>(json_value.clone())
|
||||
.ok()
|
||||
.map(PermissionConstraints::Shell),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ExtensionPreview {
|
||||
pub manifest: ExtensionManifest,
|
||||
pub is_valid_signature: bool,
|
||||
pub key_hash: String,
|
||||
pub editable_permissions: EditablePermissions,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, TS)]
|
||||
#[ts(export)]
|
||||
pub struct ExtensionInfoResponse {
|
||||
pub key_hash: String,
|
||||
pub name: String,
|
||||
@ -231,6 +189,7 @@ impl ExtensionInfoResponse {
|
||||
pub fn from_extension(
|
||||
extension: &crate::extension::core::types::Extension,
|
||||
) -> Result<Self, ExtensionError> {
|
||||
// Annahme: get_tauri_origin ist in deinem `types`-Modul oder woanders definiert
|
||||
use crate::extension::core::types::get_tauri_origin;
|
||||
|
||||
let allowed_origin = get_tauri_origin();
|
||||
|
||||
Reference in New Issue
Block a user