From 5ac21f0f2c16bb24ca4b15285186c72b5d359e79 Mon Sep 17 00:00:00 2001 From: Jordan Santell Date: Thu, 3 Aug 2023 13:05:21 -0700 Subject: [PATCH] fix: Ensure write access when syncing a sphere. Fixes #389 --- rust/noosphere-sphere/src/context.rs | 16 +++++++++ rust/noosphere-sphere/src/sync/error.rs | 3 ++ rust/noosphere-sphere/src/sync/write.rs | 45 ++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/rust/noosphere-sphere/src/context.rs b/rust/noosphere-sphere/src/context.rs index 93da7ff74..f8a778470 100644 --- a/rust/noosphere-sphere/src/context.rs +++ b/rust/noosphere-sphere/src/context.rs @@ -268,6 +268,7 @@ mod tests { use crate::{ helpers::{make_valid_link_record, simulated_sphere_context, SimulationAccess}, HasMutableSphereContext, HasSphereContext, SphereContentWrite, SpherePetnameWrite, + SphereSync, SyncError, }; #[cfg(target_arch = "wasm32")] @@ -382,4 +383,19 @@ mod tests { Ok(()) } + + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test)] + #[cfg_attr(not(target_arch = "wasm32"), tokio::test)] + 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-sphere/src/sync/error.rs b/rust/noosphere-sphere/src/sync/error.rs index fadf93721..d9cbdac9e 100644 --- a/rust/noosphere-sphere/src/sync/error.rs +++ b/rust/noosphere-sphere/src/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-sphere/src/sync/write.rs b/rust/noosphere-sphere/src/sync/write.rs index c68da3338..19afde407 100644 --- a/rust/noosphere-sphere/src/sync/write.rs +++ b/rust/noosphere-sphere/src/sync/write.rs @@ -1,8 +1,12 @@ use anyhow::Result; use async_trait::async_trait; -use noosphere_core::data::{Link, MemoIpld}; +use noosphere_core::{ + authority::Authorization, + data::{Link, MemoIpld}, +}; use noosphere_storage::Storage; +use crate::internal::SphereContextInternal; use crate::{HasMutableSphereContext, SyncError, SyncRecovery}; use crate::GatewaySyncStrategy; @@ -36,6 +40,17 @@ where async fn sync(&mut self, recovery: SyncRecovery) -> 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 { @@ -78,3 +93,31 @@ where Ok(version) } } + +/// Given a `HasMutableSphereContext`, 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: HasMutableSphereContext, + 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(_)) +}