v0.2.6-pre.011

This commit is contained in:
2026-08-21 15:22:42 +02:00
parent 6dcd278459
commit 8ba554306a
14 changed files with 572 additions and 30 deletions

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/src/app_state.rs
// version: 14
// version: 15
//! Shared backend state owned by the Wallet Desk Tauri application.
@@ -722,6 +722,46 @@ impl AppState {
return self.finish_owner_operation(context, result).await;
}
/// Strongly disables VIEW from the current OWNER session while preserving OWNER authorization.
pub(crate) async fn disable_wallet_view(&self) -> ksp_core_lib::Result<crate::WalletViewSecurityStatusDto> {
let context = self.begin_owner_operation("view_disable");
let mut context = match context {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let result = context.owner.disable_view(context.path.as_path()).await;
if result.is_ok() {
context.result_view_enabled = false;
}
let wallet = self.finish_owner_operation(context, result).await;
return match wallet {
std::result::Result::Ok(value) => std::result::Result::Ok(crate::view_security_status_from_authorized(&value)),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
/// Strongly recreates VIEW from the current OWNER session with a fresh metadata key, slot and password.
pub(crate) async fn recreate_wallet_view(
&self,
request: crate::WalletPasswordRotationRequestDto,
) -> ksp_core_lib::Result<crate::WalletViewSecurityStatusDto> {
let context = self.begin_owner_operation("view_recreate");
let mut context = match context {
std::result::Result::Ok(value) => value,
std::result::Result::Err(error) => return std::result::Result::Err(error),
};
let password = ksp_wallet_lib::ViewPassword::new(request.password);
let result = context.owner.recreate_view(context.path.as_path(), password).await;
if result.is_ok() {
context.result_view_enabled = true;
}
let wallet = self.finish_owner_operation(context, result).await;
return match wallet {
std::result::Result::Ok(value) => std::result::Result::Ok(crate::view_security_status_from_authorized(&value)),
std::result::Result::Err(error) => std::result::Result::Err(error),
};
}
/// Rotates the OWNER password while preserving the current authorized identity and metadata projection.
pub(crate) async fn rotate_owner_password(&self, request: crate::WalletPasswordRotationRequestDto) -> ksp_core_lib::Result<crate::WalletAuthorizedDto> {
let context = self.begin_owner_operation("owner_password_rotate");
@@ -894,7 +934,15 @@ impl AppState {
},
};
let pubkey = owner.pubkey().to_owned();
let context = OwnerOperationContext { operation, owner, path: path.clone(), pubkey, view_enabled, wallet_id: wallet_id.clone() };
let context = OwnerOperationContext {
operation,
owner,
path: path.clone(),
pubkey,
result_view_enabled: view_enabled,
view_enabled,
wallet_id: wallet_id.clone(),
};
*session = crate::WalletSession::OwnerOperation { wallet_id, path, pubkey, view_enabled };
ksp_logging_lib::debug!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, wallet_id = context.wallet_id.as_str(), operation, "Wallet OWNER privileged operation started");
return std::result::Result::Ok(context);
@@ -917,9 +965,9 @@ impl AppState {
}
fn install_owner_after_operation_success(&self, context: OwnerOperationContext) -> ksp_core_lib::Result<crate::WalletAuthorizedDto> {
let OwnerOperationContext { operation, owner, path, pubkey, view_enabled, wallet_id } = context;
let OwnerOperationContext { operation, owner, path, pubkey, result_view_enabled, view_enabled, wallet_id } = context;
let configured_secret_candidate_count = self.secret_candidate_count_or_zero(wallet_id.as_str());
let dto = crate::owner_projection(wallet_id.as_str(), view_enabled, configured_secret_candidate_count, &owner);
let dto = crate::owner_projection(wallet_id.as_str(), result_view_enabled, configured_secret_candidate_count, &owner);
let session = self.wallet_session.lock();
let mut session = match session {
std::result::Result::Ok(value) => value,
@@ -933,7 +981,7 @@ impl AppState {
pubkey: reserved_pubkey,
view_enabled: reserved_view_enabled,
} if reserved_wallet_id == wallet_id && reserved_path == path && reserved_pubkey == pubkey && reserved_view_enabled == view_enabled => {
*session = crate::WalletSession::Owner { wallet_id: wallet_id.clone(), path, view_enabled, wallet: owner };
*session = crate::WalletSession::Owner { wallet_id: wallet_id.clone(), path, view_enabled: result_view_enabled, wallet: owner };
ksp_logging_lib::info!(target: crate::TRACING_TARGET, domain = crate::TRACING_DOMAIN_WALLET_SESSION, wallet_id = wallet_id.as_str(), operation, "Wallet OWNER privileged operation completed");
return std::result::Result::Ok(dto);
},
@@ -949,7 +997,7 @@ impl AppState {
}
fn restore_owner_after_operation_failure(&self, context: OwnerOperationContext) {
let OwnerOperationContext { operation, owner, path, pubkey, view_enabled, wallet_id } = context;
let OwnerOperationContext { operation, owner, path, pubkey, result_view_enabled: _, view_enabled, wallet_id } = context;
let session = self.wallet_session.lock();
let mut session = match session {
std::result::Result::Ok(value) => value,
@@ -974,7 +1022,7 @@ impl AppState {
}
async fn recover_owner_state_conflict(&self, context: OwnerOperationContext) {
let OwnerOperationContext { operation, owner, path, pubkey, view_enabled, wallet_id } = context;
let OwnerOperationContext { operation, owner, path, pubkey, result_view_enabled: _, view_enabled, wallet_id } = context;
drop(owner);
let inspected = ksp_wallet_lib::inspect_locked_wallet_file_v1(path.as_path()).await;
let session = self.wallet_session.lock();
@@ -1144,6 +1192,7 @@ struct OwnerOperationContext {
owner: std::boxed::Box<ksp_wallet_lib::WalletOwner>,
path: std::path::PathBuf,
pubkey: ksp_core_lib::Pubkey,
result_view_enabled: bool,
view_enabled: bool,
wallet_id: String,
}

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/src/lib.rs
// version: 11
// version: 12
//! Tauri desktop application shell for KSP Wallet management and inspection.
@@ -202,6 +202,10 @@ pub(crate) use self::wallet_secrets::discover_wallet_secret_candidates;
pub(crate) use self::wallet_secrets::wallet_secret_candidate_count;
/// New credential request moved frontend -> Rust only for one explicit VIEW/OWNER-authorized rotation.
pub(crate) use self::wallet_security::WalletPasswordRotationRequestDto;
/// Safe VIEW security state returned after OWNER-authorized strong disable/recreate operations.
pub(crate) use self::wallet_security::WalletViewSecurityStatusDto;
/// Builds the safe strong VIEW administration status from the resulting OWNER projection.
pub(crate) use self::wallet_security::view_security_status_from_authorized;
/// Authorized OWNER projection exposed only after successful creation.
pub(crate) use self::wallet_session::WalletAuthorizedDto;
/// Create request whose password strings move frontend -> Rust only.

View File

@@ -1,5 +1,5 @@
// file: crates/ksp-app-wallet-desk/src/tauri.rs
// version: 8
// version: 9
//! Tauri runtime assembly for the KSP wallet desktop application.
@@ -45,12 +45,14 @@ fn configure_commands(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tau
create_wallet,
delete_wallet_note,
deselect_wallet,
disable_wallet_view,
emit_frontend_log,
get_runtime_status,
import_wallet,
inspect_import_source,
list_wallets,
lock_wallet,
recreate_wallet_view,
refresh_wallet_balance,
refresh_wallets,
rotate_owner_password,
@@ -135,6 +137,15 @@ fn deselect_wallet(state: tauri::State<'_, crate::AppState>) -> std::result::Res
};
}
#[tauri::command]
async fn disable_wallet_view(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::WalletViewSecurityStatusDto, crate::CommandErrorDto> {
let result = state.disable_wallet_view().await;
return match result {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
};
}
#[tauri::command]
fn emit_frontend_log(payload: crate::FrontendLogPayloadDto) -> std::result::Result<(), crate::CommandErrorDto> {
let result = crate::emit_frontend_log_event(payload);
@@ -206,6 +217,18 @@ async fn inspect_import_source(
};
}
#[tauri::command]
async fn recreate_wallet_view(
request: crate::WalletPasswordRotationRequestDto,
state: tauri::State<'_, crate::AppState>,
) -> std::result::Result<crate::WalletViewSecurityStatusDto, crate::CommandErrorDto> {
let result = state.recreate_wallet_view(request).await;
return match result {
std::result::Result::Ok(value) => std::result::Result::Ok(value),
std::result::Result::Err(error) => std::result::Result::Err(crate::CommandErrorDto::from_error(&error)),
};
}
#[tauri::command]
async fn refresh_wallet_balance(state: tauri::State<'_, crate::AppState>) -> std::result::Result<crate::WalletBalanceDto, crate::CommandErrorDto> {
let result = state.refresh_wallet_balance().await;

View File

@@ -1,10 +1,35 @@
// file: crates/ksp-app-wallet-desk/src/wallet_security.rs
// version: 2
// version: 3
//! VIEW/OWNER credential rotation contracts for Wallet Desk.
//! VIEW/OWNER credential and strong VIEW security contracts for Wallet Desk.
use ts_rs::TS; // rust-rules: trait-import
/// Safe VIEW security status returned after one OWNER-authorized strong VIEW administration operation.
#[derive(serde::Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export, export_to = "../frontend/ts/bindings/ksp_app_wallet_desk/wallet_security/WalletViewSecurityStatusDto.ts")]
pub(crate) struct WalletViewSecurityStatusDto {
/// Capability that performed the strong VIEW administration operation.
pub(crate) capability: String,
/// Number of effective Config-owned Wallet password candidates without names or values.
pub(crate) configured_secret_candidate_count: usize,
/// Whether the current Wallet state exposes a VIEW slot after the operation.
pub(crate) view_enabled: bool,
/// Root-scoped Wallet identifier.
pub(crate) wallet_id: String,
}
/// Builds the safe strong VIEW administration status from an authorized OWNER projection.
pub(crate) fn view_security_status_from_authorized(wallet: &crate::WalletAuthorizedDto) -> WalletViewSecurityStatusDto {
return WalletViewSecurityStatusDto {
capability: wallet.capability.clone(),
configured_secret_candidate_count: wallet.configured_secret_candidate_count,
view_enabled: wallet.view_enabled,
wallet_id: wallet.wallet_id.clone(),
};
}
/// New credential moved frontend -> Rust for one explicit Wallet password rotation.
#[derive(serde::Deserialize, TS)]
#[serde(rename_all = "camelCase")]