diff --git a/server/src/api/v1/upload_path.rs b/server/src/api/v1/upload_path.rs index 7c6e2bd..efa2ed8 100644 --- a/server/src/api/v1/upload_path.rs +++ b/server/src/api/v1/upload_path.rs @@ -114,7 +114,7 @@ pub(crate) async fn upload_path( match existing_nar { Some(existing_nar) => { // Deduplicate - upload_path_dedup(username, cache, upload_info, stream, existing_nar, database).await + upload_path_dedup(username, cache, upload_info, stream, database, &state, existing_nar).await } None => { // New NAR @@ -129,24 +129,27 @@ async fn upload_path_dedup( cache: cache::Model, upload_info: UploadPathNarInfo, stream: impl AsyncRead + Unpin, - existing_nar: NarGuard, database: &DatabaseConnection, + state: &State, + existing_nar: NarGuard, ) -> ServerResult { - let (mut stream, nar_compute) = StreamHasher::new(stream, Sha256::new()); - tokio::io::copy(&mut stream, &mut tokio::io::sink()) - .await - .map_err(ServerError::request_error)?; + if state.config.require_proof_of_possession { + let (mut stream, nar_compute) = StreamHasher::new(stream, Sha256::new()); + tokio::io::copy(&mut stream, &mut tokio::io::sink()) + .await + .map_err(ServerError::request_error)?; - // FIXME: errors - let (nar_hash, nar_size) = nar_compute.get().unwrap(); - let nar_hash = Hash::Sha256(nar_hash.as_slice().try_into().unwrap()); + // FIXME: errors + let (nar_hash, nar_size) = nar_compute.get().unwrap(); + let nar_hash = Hash::Sha256(nar_hash.as_slice().try_into().unwrap()); - // Confirm that the NAR Hash and Size are correct - if nar_hash.to_typed_base16() != existing_nar.nar_hash - || *nar_size != upload_info.nar_size - || *nar_size != existing_nar.nar_size as usize - { - return Err(ErrorKind::RequestError(anyhow!("Bad NAR Hash or Size")).into()); + // Confirm that the NAR Hash and Size are correct + if nar_hash.to_typed_base16() != existing_nar.nar_hash + || *nar_size != upload_info.nar_size + || *nar_size != existing_nar.nar_size as usize + { + return Err(ErrorKind::RequestError(anyhow!("Bad NAR Hash or Size")).into()); + } } // Finally... diff --git a/server/src/config-template.toml b/server/src/config-template.toml index ea1de74..954da07 100644 --- a/server/src/config-template.toml +++ b/server/src/config-template.toml @@ -27,6 +27,13 @@ allowed-hosts = [] # are there. #soft-delete-caches = false +# Whether to require fully uploading a NAR if it exists in the global cache. +# +# If set to false, simply knowing the NAR hash is enough for +# an uploader to gain access to an existing NAR in the global +# cache. +#require-proof-of-possession = true + # JWT signing token # # Set this to the Base64 encoding of some random data. diff --git a/server/src/config.rs b/server/src/config.rs index ee6f88c..c840a29 100644 --- a/server/src/config.rs +++ b/server/src/config.rs @@ -64,6 +64,15 @@ pub struct Config { #[serde(default = "default_soft_delete_caches")] pub soft_delete_caches: bool, + /// Whether to require fully uploading a NAR if it exists in the global cache. + /// + /// If set to false, simply knowing the NAR hash is enough for + /// an uploader to gain access to an existing NAR in the global + /// cache. + #[serde(rename = "require-proof-of-possession")] + #[serde(default = "default_require_proof_of_possession")] + pub require_proof_of_possession: bool, + /// Database connection. pub database: DatabaseConfig, @@ -238,6 +247,10 @@ fn default_soft_delete_caches() -> bool { false } +fn default_require_proof_of_possession() -> bool { + true +} + fn default_gc_interval() -> Duration { Duration::from_secs(43200) }