Skip to content
This repository has been archived by the owner on Sep 21, 2024. It is now read-only.

Commit

Permalink
fix: Ensure write access when syncing a sphere. Fixes #389
Browse files Browse the repository at this point in the history
  • Loading branch information
jsantell committed Oct 9, 2023
1 parent 43b325c commit 93bea2c
Show file tree
Hide file tree
Showing 3 changed files with 65 additions and 6 deletions.
16 changes: 15 additions & 1 deletion rust/noosphere-core/src/context/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -257,6 +257,7 @@ mod tests {
authority::{generate_capability, generate_ed25519_key, Access, SphereAbility},
context::{
HasMutableSphereContext, HasSphereContext, SphereContentWrite, SpherePetnameWrite,
SphereSync, SyncError,
},
data::{ContentType, LinkRecord, LINK_RECORD_FACT_NAME},
helpers::{make_valid_link_record, simulated_sphere_context},
Expand All @@ -268,7 +269,6 @@ mod tests {
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);

Expand Down Expand Up @@ -438,4 +438,18 @@ mod tests {
.is_err());
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(Access::ReadOnly, None).await?;

assert!(matches!(
sphere_context.sync().await,
Err(SyncError::InsufficientPermission)
));
Ok(())
}
}
3 changes: 3 additions & 0 deletions rust/noosphere-core/src/context/sync/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
52 changes: 47 additions & 5 deletions rust/noosphere-core/src/context/sync/write.rs
Original file line number Diff line number Diff line change
@@ -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))]
Expand Down Expand Up @@ -52,6 +55,17 @@ where
) -> Result<Link<MemoIpld>, 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 {
Expand Down Expand Up @@ -94,3 +108,31 @@ where
Ok(version)
}
}

/// Given a `HasSphereContext<S>`, 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<C, S>(context: &C) -> bool
where
C: HasSphereContext<S>,
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(_))
}

0 comments on commit 93bea2c

Please sign in to comment.