diff --git a/rust/noosphere-core/src/context/context.rs b/rust/noosphere-core/src/context/context.rs
index c8f62e1f6..d649d4075 100644
--- a/rust/noosphere-core/src/context/context.rs
+++ b/rust/noosphere-core/src/context/context.rs
@@ -262,13 +262,13 @@ mod tests {
helpers::{make_valid_link_record, simulated_sphere_context},
tracing::initialize_tracing,
view::Sphere,
+ SphereSync, SyncError,
};
use noosphere_storage::{MemoryStorage, SphereDb};
use ucan::{builder::UcanBuilder, crypto::KeyMaterial, store::UcanJwtStore};
#[cfg(target_arch = "wasm32")]
use wasm_bindgen_test::wasm_bindgen_test;
-
#[cfg(target_arch = "wasm32")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
@@ -436,6 +436,18 @@ mod tests {
.set_petname_record("foo", records.get(1).unwrap())
.await
.is_err());
+ }
+
+ async fn it_requires_write_access_to_sync() -> Result<()> {
+ initialize_tracing(None);
+
+ let (mut sphere_context, _) =
+ simulated_sphere_context(SimulationAccess::Readonly, None).await?;
+
+ assert!(matches!(
+ sphere_context.sync(crate::SyncRecovery::None).await,
+ Err(SyncError::InsufficientPermission)
+ ));
Ok(())
}
}
diff --git a/rust/noosphere-core/src/context/sync/error.rs b/rust/noosphere-core/src/context/sync/error.rs
index 5295155cc..0b20e518b 100644
--- a/rust/noosphere-core/src/context/sync/error.rs
+++ b/rust/noosphere-core/src/context/sync/error.rs
@@ -5,6 +5,9 @@ use thiserror::Error;
/// gateway
#[derive(Error, Debug)]
pub enum SyncError {
+ /// The error was due to not having write access to the sphere
+ #[error("Insufficient permission to sync")]
+ InsufficientPermission,
/// The error was a conflict; this is possibly recoverable
#[error("There was a conflict during sync")]
Conflict,
diff --git a/rust/noosphere-core/src/context/sync/write.rs b/rust/noosphere-core/src/context/sync/write.rs
index 1d075a3bd..9c040cf37 100644
--- a/rust/noosphere-core/src/context/sync/write.rs
+++ b/rust/noosphere-core/src/context/sync/write.rs
@@ -1,12 +1,15 @@
-use crate::data::{Link, MemoIpld};
+use crate::{
+ authority::Authorization,
+ context::{
+ internal::SphereContextInternal, GatewaySyncStrategy, HasMutableSphereContext,
+ HasSphereContext, SyncError, SyncExtent, SyncRecovery,
+ },
+ data::{Link, MemoIpld},
+};
use anyhow::Result;
use async_trait::async_trait;
use noosphere_storage::Storage;
-use crate::context::{HasMutableSphereContext, SyncError, SyncExtent, SyncRecovery};
-
-use crate::context::GatewaySyncStrategy;
-
/// Implementors of [SphereSync] are able to sychronize with a Noosphere gateway
#[cfg_attr(not(target_arch = "wasm32"), async_trait)]
#[cfg_attr(target_arch = "wasm32", async_trait(?Send))]
@@ -52,6 +55,17 @@ where
) -> Result, SyncError> {
debug!("Attempting to sync...");
+ // Check that the author has write access to sync.
+ // If a sphere was joined from another sphere, do not check,
+ // but allow sync to proceed, as the local sphere does not have
+ // local proof until after initial sync. If truly no write access is
+ // available, the gateway will reject this sync.
+ if !is_sphere_joined(self).await {
+ self.assert_write_access()
+ .await
+ .map_err(|_| SyncError::InsufficientPermission)?;
+ }
+
let sync_strategy = GatewaySyncStrategy::default();
let version = match recovery {
@@ -94,3 +108,31 @@ where
Ok(version)
}
}
+
+/// Given a `HasSphereContext`, return a boolean indicating
+/// whether or not this sphere has been joined from another sphere
+/// (e.g. possibly lacking local authorization until syncing with a gateway).
+async fn is_sphere_joined(context: &C) -> bool
+where
+ C: HasSphereContext,
+ S: Storage + 'static,
+{
+ let context = {
+ let context = context.sphere_context().await;
+ if context.is_err() {
+ return false;
+ }
+ context.unwrap()
+ };
+
+ let author = context.author();
+
+ let auth = {
+ let auth = author.require_authorization();
+ if auth.is_err() {
+ return false;
+ }
+ auth.unwrap()
+ };
+ matches!(auth, Authorization::Cid(_))
+}