mirror of
https://github.com/sudosylabs/vnidrop.git
synced 2026-08-13 05:49:57 +02:00
refactor(core): remove experimental saved device contract
This commit is contained in:
@@ -161,6 +161,9 @@ final class SavedDeviceCoreContractTests: XCTestCase {
|
|||||||
"generated bindings must not expose \(needle)"
|
"generated bindings must not expose \(needle)"
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
XCTAssertFalse(source.contains("initializeWithExperimentalSavedDevices"))
|
||||||
|
XCTAssertFalse(source.contains("ExperimentalSavedDeviceCapabilities"))
|
||||||
|
XCTAssertFalse(source.contains("experimentalSavedDeviceCapabilities"))
|
||||||
XCTAssertTrue(source.contains("initializeWithLimitsAndNetworkConfig"))
|
XCTAssertTrue(source.contains("initializeWithLimitsAndNetworkConfig"))
|
||||||
XCTAssertTrue(source.contains("public struct SavedDeviceCapabilities"))
|
XCTAssertTrue(source.contains("public struct SavedDeviceCapabilities"))
|
||||||
XCTAssertTrue(source.contains("public func savedDeviceCapabilities()"))
|
XCTAssertTrue(source.contains("public func savedDeviceCapabilities()"))
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ src/
|
|||||||
receive.rs # receive, download, export, OutputSinkFile
|
receive.rs # receive, download, export, OutputSinkFile
|
||||||
lifecycle.rs # cancel share, delete, status, access mode, shutdown
|
lifecycle.rs # cancel share, delete, status, access mode, shutdown
|
||||||
provider.rs # provider messages, per-peer transfer progress
|
provider.rs # provider messages, per-peer transfer progress
|
||||||
saved_devices.rs # experimental saved-device pairing, forget, block
|
saved_devices.rs # saved-device pairing, forget, block
|
||||||
targeted.rs # saved-device targeted transfers
|
targeted.rs # saved-device targeted transfers
|
||||||
persistence.rs # AppDataStores / persistence open (domain stores)
|
persistence.rs # AppDataStores / persistence open (domain stores)
|
||||||
invitation/ # invitation-transfer domain store (type name: Repository)
|
invitation/ # invitation-transfer domain store (type name: Repository)
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ bytes through Kotlin memory.
|
|||||||
`stopped`.
|
`stopped`.
|
||||||
- Main event phases: `endpoint`, `import`, `ticket`, `handshake`, `approval`,
|
- Main event phases: `endpoint`, `import`, `ticket`, `handshake`, `approval`,
|
||||||
`access`, `transfer`, `download`, `export`, `delivery`, `lifecycle`, `error`,
|
`access`, `transfer`, `download`, `export`, `delivery`, `lifecycle`, `error`,
|
||||||
plus experimental `pairing` and `targeted_transfer` (see catalog below).
|
`pairing`, and `targeted_transfer` (see catalog below).
|
||||||
- Events are sent to `CoreEventSink` immediately and persisted through the event
|
- Events are sent to `CoreEventSink` immediately and persisted through the event
|
||||||
hub. `list_events` flushes queued persistence before reading SQLite.
|
hub. `list_events` flushes queued persistence before reading SQLite.
|
||||||
- `shutdown()` is idempotent and flushes events before stopping the router.
|
- `shutdown()` is idempotent and flushes events before stopping the router.
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use iroh::RelayUrl;
|
use iroh::RelayUrl;
|
||||||
use iroh_blobs::Hash;
|
use iroh_blobs::Hash;
|
||||||
@@ -29,25 +27,6 @@ pub fn saved_device_capabilities() -> SavedDeviceCapabilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[deprecated(note = "use SavedDeviceCapabilities")]
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, uniffi::Record)]
|
|
||||||
pub struct ExperimentalSavedDeviceCapabilities {
|
|
||||||
pub domain_contract_version: u16,
|
|
||||||
pub relationship_protocol_version: u16,
|
|
||||||
pub targeted_transfer_protocol_version: u16,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[deprecated(note = "use saved_device_capabilities")]
|
|
||||||
#[uniffi::export]
|
|
||||||
pub fn experimental_saved_device_capabilities() -> ExperimentalSavedDeviceCapabilities {
|
|
||||||
let capabilities = saved_device_capabilities();
|
|
||||||
ExperimentalSavedDeviceCapabilities {
|
|
||||||
domain_contract_version: capabilities.domain_contract_version,
|
|
||||||
relationship_protocol_version: capabilities.relationship_protocol_version,
|
|
||||||
targeted_transfer_protocol_version: capabilities.targeted_transfer_protocol_version,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Public view of a single-use pairing window after a completed transfer.
|
/// Public view of a single-use pairing window after a completed transfer.
|
||||||
///
|
///
|
||||||
/// The eligibility capability itself never crosses this boundary.
|
/// The eligibility capability itself never crosses this boundary.
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Experimental saved-device mutual-consent relationships.
|
//! Saved-device mutual-consent relationships.
|
||||||
//!
|
//!
|
||||||
//! Pending outgoing/incoming states, directional grants bound to relationship
|
//! Pending outgoing/incoming states, directional grants bound to relationship
|
||||||
//! generation, and Saved only after mutual acknowledgement.
|
//! generation, and Saved only after mutual acknowledgement.
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ mod logging;
|
|||||||
mod pairing_eligibility;
|
mod pairing_eligibility;
|
||||||
mod persistence;
|
mod persistence;
|
||||||
mod runtime;
|
mod runtime;
|
||||||
#[cfg(test)]
|
|
||||||
mod secret;
|
|
||||||
#[allow(
|
#[allow(
|
||||||
dead_code,
|
dead_code,
|
||||||
reason = "the private custody seam is activated by platform credential adapters"
|
reason = "the private custody seam is activated by platform credential adapters"
|
||||||
@@ -26,15 +24,13 @@ mod ticket;
|
|||||||
mod transfer_state;
|
mod transfer_state;
|
||||||
mod util;
|
mod util;
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
pub use api::{
|
pub use api::{
|
||||||
clear_inactive_transfer_cache, default_core_limits, default_core_network_config,
|
clear_inactive_transfer_cache, default_core_limits, default_core_network_config,
|
||||||
experimental_saved_device_capabilities, saved_device_capabilities, CoreEvent, CoreEventSink,
|
saved_device_capabilities, CoreEvent, CoreEventSink, CoreLimits, CoreNetworkConfig,
|
||||||
CoreLimits, CoreNetworkConfig, CoreRelayMode, CoreStorageUsage, DeviceRelationship,
|
CoreRelayMode, CoreStorageUsage, DeviceRelationship, DeviceRelationshipState,
|
||||||
DeviceRelationshipState, ExperimentalSavedDeviceCapabilities, PairingEligibilitySummary,
|
PairingEligibilitySummary, PendingTargetedOffer, PublishedOutput, ReceiveOutputSink,
|
||||||
PendingTargetedOffer, PublishedOutput, ReceiveOutputSink, ReceiveOutputSinkV2,
|
ReceiveOutputSinkV2, ReceivedArtifact, ReceivedLocatorKind, ReceiverRequest, RuntimeStatus,
|
||||||
ReceivedArtifact, ReceivedLocatorKind, ReceiverRequest, RuntimeStatus, SavedDevice,
|
SavedDevice, SavedDeviceCapabilities, ShareMetadataInput, ShareResult, ShareSource, SourceKind,
|
||||||
SavedDeviceCapabilities, ShareMetadataInput, ShareResult, ShareSource, SourceKind,
|
|
||||||
StoredTransfer, TargetedOfferResponse, TargetedTransfer, TargetedTransferState,
|
StoredTransfer, TargetedOfferResponse, TargetedTransfer, TargetedTransferState,
|
||||||
TicketInspection, TransferAccessMode, TransferMetadata,
|
TicketInspection, TransferAccessMode, TransferMetadata,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use std::{future::Future, path::PathBuf, sync::Arc};
|
use std::{future::Future, path::PathBuf, sync::Arc};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
@@ -317,7 +315,6 @@ impl VnidropCore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(deprecated)]
|
|
||||||
#[uniffi::export]
|
#[uniffi::export]
|
||||||
impl VnidropCore {
|
impl VnidropCore {
|
||||||
#[uniffi::constructor]
|
#[uniffi::constructor]
|
||||||
@@ -371,18 +368,6 @@ impl VnidropCore {
|
|||||||
Self::initialize_protected(app_data_dir, event_sink, limits, network_config)
|
Self::initialize_protected(app_data_dir, event_sink, limits, network_config)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Compatibility constructor retained during the protected-initialization expand phase.
|
|
||||||
#[deprecated(note = "use initialize_with_limits_and_network_config")]
|
|
||||||
#[uniffi::constructor]
|
|
||||||
pub fn initialize_with_experimental_saved_devices(
|
|
||||||
app_data_dir: String,
|
|
||||||
event_sink: Arc<dyn CoreEventSink>,
|
|
||||||
limits: CoreLimits,
|
|
||||||
network_config: CoreNetworkConfig,
|
|
||||||
) -> Result<Arc<Self>, VnidropError> {
|
|
||||||
Self::initialize_protected(app_data_dir, event_sink, limits, network_config)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn status(&self) -> RuntimeStatus {
|
pub fn status(&self) -> RuntimeStatus {
|
||||||
self.block_on(self.inner.status())
|
self.block_on(self.inner.status())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
//! - [`receive`] — ticket receive, download, export
|
//! - [`receive`] — ticket receive, download, export
|
||||||
//! - [`lifecycle`] — cancel/delete/shutdown/status/access
|
//! - [`lifecycle`] — cancel/delete/shutdown/status/access
|
||||||
//! - [`provider`] — blob provider events and per-connection send progress
|
//! - [`provider`] — blob provider events and per-connection send progress
|
||||||
//! - [`saved_devices`] — experimental saved-device pairing, forget, block
|
//! - [`saved_devices`] — saved-device pairing, forget, block
|
||||||
//! - [`targeted`] — saved-device targeted transfers
|
//! - [`targeted`] — saved-device targeted transfers
|
||||||
|
|
||||||
mod delivery;
|
mod delivery;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Runtime operations for experimental saved devices and device relationships.
|
//! Runtime operations for saved devices and device relationships.
|
||||||
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
use std::{
|
|
||||||
fs::OpenOptions,
|
|
||||||
io::{self, Write},
|
|
||||||
path::Path,
|
|
||||||
str::FromStr,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
use std::os::unix::fs::{OpenOptionsExt, PermissionsExt};
|
|
||||||
|
|
||||||
use anyhow::{Context, Result};
|
|
||||||
use data_encoding::HEXLOWER;
|
|
||||||
use iroh::SecretKey;
|
|
||||||
|
|
||||||
pub(crate) async fn load_or_create_secret(app_data_dir: &Path) -> Result<SecretKey> {
|
|
||||||
if let Ok(secret) = std::env::var("IROH_SECRET") {
|
|
||||||
return SecretKey::from_str(&secret).context("invalid IROH_SECRET");
|
|
||||||
}
|
|
||||||
|
|
||||||
let path = app_data_dir.join("iroh.secret");
|
|
||||||
match tokio::fs::read_to_string(&path).await {
|
|
||||||
Ok(secret) => {
|
|
||||||
let bytes = HEXLOWER
|
|
||||||
.decode(secret.trim().as_bytes())
|
|
||||||
.context("invalid persisted iroh secret encoding")?;
|
|
||||||
let bytes: [u8; 32] = bytes
|
|
||||||
.try_into()
|
|
||||||
.map_err(|_| anyhow::anyhow!("invalid persisted iroh secret length"))?;
|
|
||||||
restrict_permissions(&path).await?;
|
|
||||||
Ok(SecretKey::from_bytes(&bytes))
|
|
||||||
}
|
|
||||||
Err(error) if error.kind() == io::ErrorKind::NotFound => {
|
|
||||||
let secret = SecretKey::generate();
|
|
||||||
let encoded = HEXLOWER.encode(&secret.to_bytes());
|
|
||||||
// Create with owner-only mode on Unix so the key is never briefly 0644.
|
|
||||||
write_secret_file(&path, encoded.as_bytes()).await?;
|
|
||||||
restrict_permissions(&path).await?;
|
|
||||||
Ok(secret)
|
|
||||||
}
|
|
||||||
Err(error) => Err(error.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn write_secret_file(path: &Path, bytes: &[u8]) -> Result<()> {
|
|
||||||
let path = path.to_path_buf();
|
|
||||||
let bytes = bytes.to_vec();
|
|
||||||
tokio::task::spawn_blocking(move || {
|
|
||||||
let mut options = OpenOptions::new();
|
|
||||||
options.write(true).create_new(true);
|
|
||||||
#[cfg(unix)]
|
|
||||||
options.mode(0o600);
|
|
||||||
let mut file = options
|
|
||||||
.open(&path)
|
|
||||||
.with_context(|| format!("failed to create {}", path.display()))?;
|
|
||||||
file.write_all(&bytes)
|
|
||||||
.with_context(|| format!("failed to write {}", path.display()))?;
|
|
||||||
file.sync_all()
|
|
||||||
.with_context(|| format!("failed to sync {}", path.display()))?;
|
|
||||||
Ok::<(), anyhow::Error>(())
|
|
||||||
})
|
|
||||||
.await?
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn restrict_permissions(path: &Path) -> Result<()> {
|
|
||||||
#[cfg(unix)]
|
|
||||||
tokio::fs::set_permissions(path, std::fs::Permissions::from_mode(0o600)).await?;
|
|
||||||
// Windows: file lives under the user profile app-data dir with default ACLs
|
|
||||||
// limited to the current user. No portable owner-only API in std.
|
|
||||||
let _ = path;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
@@ -37,8 +37,6 @@ mod platform_contract_windows_tests;
|
|||||||
mod repository_tests;
|
mod repository_tests;
|
||||||
#[path = "tests/runtime.rs"]
|
#[path = "tests/runtime.rs"]
|
||||||
mod runtime_tests;
|
mod runtime_tests;
|
||||||
#[path = "tests/secret.rs"]
|
|
||||||
mod secret_tests;
|
|
||||||
#[path = "tests/secure_secret_android.rs"]
|
#[path = "tests/secure_secret_android.rs"]
|
||||||
mod secure_secret_android_tests;
|
mod secure_secret_android_tests;
|
||||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ fn public_api_exposes_saved_device_surface_without_prototype_contact_entry_point
|
|||||||
let lib = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/lib.rs"));
|
let lib = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/src/lib.rs"));
|
||||||
|
|
||||||
for forbidden in [
|
for forbidden in [
|
||||||
|
"initialize_with_experimental_saved_devices",
|
||||||
|
"experimental_saved_device_capabilities",
|
||||||
|
"ExperimentalSavedDeviceCapabilities",
|
||||||
"fn list_contacts(",
|
"fn list_contacts(",
|
||||||
"fn send_to_contact(",
|
"fn send_to_contact(",
|
||||||
"fn poll_contacts_for_offers(",
|
"fn poll_contacts_for_offers(",
|
||||||
@@ -58,7 +61,6 @@ fn public_api_exposes_saved_device_surface_without_prototype_contact_entry_point
|
|||||||
"fn forget_saved_device(",
|
"fn forget_saved_device(",
|
||||||
"fn share_files(",
|
"fn share_files(",
|
||||||
"fn receive(",
|
"fn receive(",
|
||||||
"experimental_saved_device_capabilities",
|
|
||||||
"saved_device_capabilities",
|
"saved_device_capabilities",
|
||||||
] {
|
] {
|
||||||
assert!(
|
assert!(
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
//! Android platform contract for the experimental saved-device foundation.
|
//! Android platform contract for the saved-device foundation.
|
||||||
//!
|
//!
|
||||||
//! These tests drive the public UniFFI surface through an
|
//! These tests drive the public UniFFI surface through an
|
||||||
//! [`AndroidSecureSecretStore`] backed by an in-process Keystore fake so the
|
//! [`AndroidSecureSecretStore`] backed by an in-process Keystore fake so the
|
||||||
@@ -929,6 +929,9 @@ fn android_public_surface_omits_raw_secrets_and_generic_mutation() {
|
|||||||
path.display()
|
path.display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
assert!(!kotlin.contains("initializeWithExperimentalSavedDevices"));
|
||||||
|
assert!(!kotlin.contains("ExperimentalSavedDeviceCapabilities"));
|
||||||
|
assert!(!kotlin.contains("experimentalSavedDeviceCapabilities"));
|
||||||
assert!(
|
assert!(
|
||||||
kotlin.contains("initializeWithLimitsAndNetworkConfig"),
|
kotlin.contains("initializeWithLimitsAndNetworkConfig"),
|
||||||
"production protected Android init must remain on the public binding surface"
|
"production protected Android init must remain on the public binding surface"
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ impl RecordingSink {
|
|||||||
/// `AppleKeychainSecretStore` + profile scoping. CLI unit tests lack the app
|
/// `AppleKeychainSecretStore` + profile scoping. CLI unit tests lack the app
|
||||||
/// Keychain entitlement, so the system Keychain returns Unavailable; the
|
/// Keychain entitlement, so the system Keychain returns Unavailable; the
|
||||||
/// injectable API exercises the identical adapter path. Swift XCTest covers
|
/// injectable API exercises the identical adapter path. Swift XCTest covers
|
||||||
/// the real experimental constructor under the app entitlements.
|
/// the standard protected constructor under the app entitlements.
|
||||||
struct KeychainNode {
|
struct KeychainNode {
|
||||||
data_dir: tempfile::TempDir,
|
data_dir: tempfile::TempDir,
|
||||||
api: RecordingKeychain,
|
api: RecordingKeychain,
|
||||||
@@ -208,7 +208,7 @@ impl Drop for KeychainNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn try_experimental_keychain_init(app_data_dir: &Path) -> Result<Arc<VnidropCore>, VnidropError> {
|
fn try_keychain_init(app_data_dir: &Path) -> Result<Arc<VnidropCore>, VnidropError> {
|
||||||
VnidropCore::initialize_with_limits_and_network_config(
|
VnidropCore::initialize_with_limits_and_network_config(
|
||||||
app_data_dir.to_string_lossy().into_owned(),
|
app_data_dir.to_string_lossy().into_owned(),
|
||||||
Arc::new(RecordingSink {
|
Arc::new(RecordingSink {
|
||||||
@@ -593,7 +593,7 @@ fn recover_authoritative_state(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn experimental_keychain_identity_survives_core_restart() {
|
fn keychain_identity_survives_core_restart() {
|
||||||
let node = KeychainNode::new();
|
let node = KeychainNode::new();
|
||||||
let endpoint_id = node.core().status().endpoint_id.clone();
|
let endpoint_id = node.core().status().endpoint_id.clone();
|
||||||
assert!(!endpoint_id.is_empty());
|
assert!(!endpoint_id.is_empty());
|
||||||
@@ -610,18 +610,18 @@ fn experimental_keychain_identity_survives_core_restart() {
|
|||||||
// constructor must also preserve identity. Headless cargo often lacks that
|
// constructor must also preserve identity. Headless cargo often lacks that
|
||||||
// entitlement and maps it to Unavailable — that path is covered by Swift.
|
// entitlement and maps it to Unavailable — that path is covered by Swift.
|
||||||
let live = tempfile::tempdir().unwrap();
|
let live = tempfile::tempdir().unwrap();
|
||||||
match try_experimental_keychain_init(live.path()) {
|
match try_keychain_init(live.path()) {
|
||||||
Ok(core) => {
|
Ok(core) => {
|
||||||
let id = core.status().endpoint_id.clone();
|
let id = core.status().endpoint_id.clone();
|
||||||
core.shutdown();
|
core.shutdown();
|
||||||
drop(core);
|
drop(core);
|
||||||
let restarted = try_experimental_keychain_init(live.path()).expect("restart");
|
let restarted = try_keychain_init(live.path()).expect("restart");
|
||||||
assert_eq!(restarted.status().endpoint_id, id);
|
assert_eq!(restarted.status().endpoint_id, id);
|
||||||
restarted.shutdown();
|
restarted.shutdown();
|
||||||
cleanup_scoped_keychain(live.path());
|
cleanup_scoped_keychain(live.path());
|
||||||
}
|
}
|
||||||
Err(VnidropError::SecureStorageUnavailable { .. }) => {}
|
Err(VnidropError::SecureStorageUnavailable { .. }) => {}
|
||||||
Err(error) => panic!("unexpected experimental init failure: {error:?}"),
|
Err(error) => panic!("unexpected protected init failure: {error:?}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -840,6 +840,9 @@ fn apple_public_bindings_omit_raw_secrets_and_generic_mutation() {
|
|||||||
swift.display()
|
swift.display()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
assert!(!source.contains("initializeWithExperimentalSavedDevices"));
|
||||||
|
assert!(!source.contains("ExperimentalSavedDeviceCapabilities"));
|
||||||
|
assert!(!source.contains("experimentalSavedDeviceCapabilities"));
|
||||||
assert!(
|
assert!(
|
||||||
source.contains("initializeWithLimitsAndNetworkConfig"),
|
source.contains("initializeWithLimitsAndNetworkConfig"),
|
||||||
"Swift bindings must expose standard protected initialization"
|
"Swift bindings must expose standard protected initialization"
|
||||||
@@ -865,6 +868,8 @@ fn apple_public_bindings_omit_raw_secrets_and_generic_mutation() {
|
|||||||
)
|
)
|
||||||
.expect("api.rs");
|
.expect("api.rs");
|
||||||
for forbidden in [
|
for forbidden in [
|
||||||
|
"ExperimentalSavedDeviceCapabilities",
|
||||||
|
"experimental_saved_device_capabilities",
|
||||||
"SecretMaterial",
|
"SecretMaterial",
|
||||||
"SecretHandle",
|
"SecretHandle",
|
||||||
"SecureSecretStore",
|
"SecureSecretStore",
|
||||||
|
|||||||
@@ -546,7 +546,7 @@ fn secret_service_identity_survives_core_restart() {
|
|||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
#[test]
|
#[test]
|
||||||
fn experimental_secret_service_identity_survives_core_restart_on_linux() {
|
fn secret_service_identity_survives_core_restart_on_linux() {
|
||||||
// Regression: protected init used to call blocking Secret Service on the
|
// Regression: protected init used to call blocking Secret Service on the
|
||||||
// Tokio worker that drives `CoreInner::start`, which nested `block_on` and
|
// Tokio worker that drives `CoreInner::start`, which nested `block_on` and
|
||||||
// aborted desktop startup with "Cannot start a runtime from within a runtime".
|
// aborted desktop startup with "Cannot start a runtime from within a runtime".
|
||||||
@@ -560,7 +560,7 @@ fn experimental_secret_service_identity_survives_core_restart_on_linux() {
|
|||||||
CoreLimits::default(),
|
CoreLimits::default(),
|
||||||
CoreNetworkConfig::default(),
|
CoreNetworkConfig::default(),
|
||||||
)
|
)
|
||||||
.expect("experimental Linux Secret Service core");
|
.expect("Linux Secret Service core");
|
||||||
let endpoint_id = core.status().endpoint_id.clone();
|
let endpoint_id = core.status().endpoint_id.clone();
|
||||||
assert!(!endpoint_id.is_empty());
|
assert!(!endpoint_id.is_empty());
|
||||||
assert!(!data_dir.path().join("iroh.secret").exists());
|
assert!(!data_dir.path().join("iroh.secret").exists());
|
||||||
@@ -585,7 +585,7 @@ fn experimental_secret_service_identity_survives_core_restart_on_linux() {
|
|||||||
{
|
{
|
||||||
std::thread::sleep(Duration::from_millis(25));
|
std::thread::sleep(Duration::from_millis(25));
|
||||||
}
|
}
|
||||||
Err(error) => panic!("restart experimental Linux core: {error:?}"),
|
Err(error) => panic!("restart Linux core: {error:?}"),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
assert_eq!(restarted.status().endpoint_id, endpoint_id);
|
assert_eq!(restarted.status().endpoint_id, endpoint_id);
|
||||||
@@ -842,7 +842,7 @@ fn linux_public_bindings_omit_raw_secrets_and_generic_mutation() {
|
|||||||
}
|
}
|
||||||
assert!(
|
assert!(
|
||||||
public_facade.contains("initialize_with_limits_and_network_config"),
|
public_facade.contains("initialize_with_limits_and_network_config"),
|
||||||
"facade must expose experimental saved-device init"
|
"facade must expose standard protected initialization"
|
||||||
);
|
);
|
||||||
assert!(
|
assert!(
|
||||||
!public_facade.contains("fn set_state(") && !public_facade.contains("fn mutate_state("),
|
!public_facade.contains("fn set_state(") && !public_facade.contains("fn mutate_state("),
|
||||||
|
|||||||
@@ -315,7 +315,7 @@ fn windows_dpapi_identity_survives_core_restart() {
|
|||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
#[test]
|
#[test]
|
||||||
fn real_windows_dpapi_experimental_init_preserves_identity() {
|
fn real_windows_dpapi_init_preserves_identity() {
|
||||||
let data_dir = tempfile::tempdir().unwrap();
|
let data_dir = tempfile::tempdir().unwrap();
|
||||||
let path = data_dir.path().to_string_lossy().into_owned();
|
let path = data_dir.path().to_string_lossy().into_owned();
|
||||||
let sink = Arc::new(RecordingSink {
|
let sink = Arc::new(RecordingSink {
|
||||||
@@ -327,7 +327,7 @@ fn real_windows_dpapi_experimental_init_preserves_identity() {
|
|||||||
CoreLimits::default(),
|
CoreLimits::default(),
|
||||||
CoreNetworkConfig::default(),
|
CoreNetworkConfig::default(),
|
||||||
)
|
)
|
||||||
.expect("experimental windows core");
|
.expect("Windows core");
|
||||||
let first = core.status().endpoint_id.clone();
|
let first = core.status().endpoint_id.clone();
|
||||||
core.shutdown();
|
core.shutdown();
|
||||||
drop(core);
|
drop(core);
|
||||||
@@ -341,7 +341,7 @@ fn real_windows_dpapi_experimental_init_preserves_identity() {
|
|||||||
CoreLimits::default(),
|
CoreLimits::default(),
|
||||||
CoreNetworkConfig::default(),
|
CoreNetworkConfig::default(),
|
||||||
)
|
)
|
||||||
.expect("restarted experimental windows core");
|
.expect("restarted Windows core");
|
||||||
assert_eq!(restarted.status().endpoint_id, first);
|
assert_eq!(restarted.status().endpoint_id, first);
|
||||||
restarted.shutdown();
|
restarted.shutdown();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
#[cfg(unix)]
|
|
||||||
use std::os::unix::fs::PermissionsExt;
|
|
||||||
|
|
||||||
use crate::secret::load_or_create_secret;
|
|
||||||
|
|
||||||
#[tokio::test]
|
|
||||||
async fn persists_with_restricted_permissions() {
|
|
||||||
let temp = tempfile::tempdir().unwrap();
|
|
||||||
let first = load_or_create_secret(temp.path()).await.unwrap();
|
|
||||||
let second = load_or_create_secret(temp.path()).await.unwrap();
|
|
||||||
assert_eq!(first.to_bytes(), second.to_bytes());
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
assert_eq!(
|
|
||||||
std::fs::metadata(temp.path().join("iroh.secret"))
|
|
||||||
.unwrap()
|
|
||||||
.permissions()
|
|
||||||
.mode()
|
|
||||||
& 0o777,
|
|
||||||
0o600
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -8,7 +8,7 @@ use vnidrop::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn saved_device_protocols_are_explicitly_experimental_and_versioned() {
|
fn saved_device_protocols_are_explicitly_versioned() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
saved_device_capabilities(),
|
saved_device_capabilities(),
|
||||||
SavedDeviceCapabilities {
|
SavedDeviceCapabilities {
|
||||||
@@ -97,7 +97,7 @@ fn targeted_transfer_transitions_are_validated_by_the_domain() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn experimental_domain_seam_does_not_change_multi_receiver_shares() {
|
fn saved_device_domain_seam_does_not_change_multi_receiver_shares() {
|
||||||
let source_dir = tempfile::tempdir().unwrap();
|
let source_dir = tempfile::tempdir().unwrap();
|
||||||
let first_output = tempfile::tempdir().unwrap();
|
let first_output = tempfile::tempdir().unwrap();
|
||||||
let second_output = tempfile::tempdir().unwrap();
|
let second_output = tempfile::tempdir().unwrap();
|
||||||
@@ -32,6 +32,9 @@ class SavedDeviceCoreContractBindingHygieneTest {
|
|||||||
"generated binding ${binding.path} must not expose `$token`",
|
"generated binding ${binding.path} must not expose `$token`",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
assertFalse(source.contains("initializeWithExperimentalSavedDevices"))
|
||||||
|
assertFalse(source.contains("ExperimentalSavedDeviceCapabilities"))
|
||||||
|
assertFalse(source.contains("experimentalSavedDeviceCapabilities"))
|
||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
source.contains("initializeWithLimitsAndNetworkConfig"),
|
source.contains("initializeWithLimitsAndNetworkConfig"),
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ class SavedDeviceCoreContractBindingHygieneTest {
|
|||||||
"generated binding ${binding.path} must not expose `$token`",
|
"generated binding ${binding.path} must not expose `$token`",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
assertFalse(source.contains("initializeWithExperimentalSavedDevices"))
|
||||||
|
assertFalse(source.contains("ExperimentalSavedDeviceCapabilities"))
|
||||||
|
assertFalse(source.contains("experimentalSavedDeviceCapabilities"))
|
||||||
|
|
||||||
assertTrue(
|
assertTrue(
|
||||||
source.contains("initializeWithLimitsAndNetworkConfig"),
|
source.contains("initializeWithLimitsAndNetworkConfig"),
|
||||||
|
|||||||
Reference in New Issue
Block a user