// Copyright 2024 The Jujutsu Authors//// Licensed under the Apache License, Version 2.0 (the "License");// you may not use this file except in compliance with the License.// You may obtain a copy of the License at//// https://www.apache.org/licenses/LICENSE-2.0//// Unless required by applicable law or agreed to in writing, software// distributed under the License is distributed on an "AS IS" BASIS,// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.// See the License for the specific language governing permissions and// limitations under the License. //! Provides a backend for testing ACLs use std::path::Path;use std::pin::Pin;use std::time::SystemTime; use async_trait::async_trait;use futures::stream::BoxStream;use tokio::io::AsyncRead; use crate::backend::Backend;use crate::backend::BackendError;use crate::backend::BackendLoadError;use crate::backend::BackendResult;use crate::backend::ChangeId;use crate::backend::Commit;use crate::backend::CommitId;use crate::backend::CopyHistory;use crate::backend::CopyId;use crate::backend::CopyRecord;use crate::backend::FileId;use crate::backend::SigningFn;use crate::backend::SymlinkId;use crate::backend::Tree;use crate::backend::TreeId;use crate::git_backend::GitBackend;use crate::index::Index;use crate::object_id::ObjectId as _;use crate::repo_path::RepoPath;use crate::repo_path::RepoPathBuf;use crate::settings::UserSettings; const SECRET_CONTENTS_HEX: [&str; 2] = [ "d97c5eada5d8c52079031eef0107a4430a9617c5", // "secret\n" "536aca34dbae6b2b8af26bebdcba83543c9546f0", // "secret"]; /// A commit backend that's completely compatible with the Git backend, except/// that it refuses to read files and symlinks with the word "secret" in the/// path, or "secret" or "secret\n" in the content.#[derive(Debug)]pub struct SecretBackend { inner: GitBackend,} impl SecretBackend { /// "secret" pub fn name() -> &'static str { "secret" } /// Loads the backend from the given path. pub fn load(settings: &UserSettings, store_path: &Path) -> Result<Self, BackendLoadError> { let inner = GitBackend::load(settings, store_path)?; Ok(Self { inner }) } /// Convert a git repo to using `SecretBackend` // TODO: Avoid this hack pub fn adopt_git_repo(workspace_path: &Path) { std::fs::write( workspace_path .join(".jj") .join("repo") .join("store") .join("type"), Self::name(), ) .unwrap(); }} #[async_trait]impl Backend for SecretBackend { fn name(&self) -> &str { Self::name() } fn commit_id_length(&self) -> usize { self.inner.commit_id_length() } fn change_id_length(&self) -> usize { self.inner.change_id_length() } fn root_commit_id(&self) -> &CommitId { self.inner.root_commit_id() } fn root_change_id(&self) -> &ChangeId { self.inner.root_change_id() } fn empty_tree_id(&self) -> &TreeId { self.inner.empty_tree_id() } fn concurrency(&self) -> usize { 1 } async fn read_file( &self, path: &RepoPath, id: &FileId, ) -> BackendResult<Pin<Box<dyn AsyncRead + Send>>> { if path.as_internal_file_string().contains("secret") || SECRET_CONTENTS_HEX.contains(&id.hex().as_ref()) { return Err(BackendError::ReadAccessDenied { object_type: "file".to_string(), hash: id.hex(), source: "No access".into(), }); } self.inner.read_file(path, id).await } async fn write_file( &self, path: &RepoPath, contents: &mut (dyn AsyncRead + Send + Unpin), ) -> BackendResult<FileId> { self.inner.write_file(path, contents).await } async fn read_symlink(&self, path: &RepoPath, id: &SymlinkId) -> BackendResult<String> { if path.as_internal_file_string().contains("secret") || SECRET_CONTENTS_HEX.contains(&id.hex().as_ref()) { return Err(BackendError::ReadAccessDenied { object_type: "symlink".to_string(), hash: id.hex(), source: "No access".into(), }); } self.inner.read_symlink(path, id).await } async fn write_symlink(&self, path: &RepoPath, target: &str) -> BackendResult<SymlinkId> { self.inner.write_symlink(path, target).await } async fn read_copy(&self, _id: &CopyId) -> BackendResult<CopyHistory> { Err(BackendError::Unsupported( "The secret backend doesn't support copies".to_string(), )) } async fn write_copy(&self, _contents: &CopyHistory) -> BackendResult<CopyId> { Err(BackendError::Unsupported( "The secret backend doesn't support copies".to_string(), )) } async fn get_related_copies(&self, _copy_id: &CopyId) -> BackendResult<Vec<CopyHistory>> { Err(BackendError::Unsupported( "The secret backend doesn't support copies".to_string(), )) } async fn read_tree(&self, path: &RepoPath, id: &TreeId) -> BackendResult<Tree> { self.inner.read_tree(path, id).await } async fn write_tree(&self, path: &RepoPath, contents: &Tree) -> BackendResult<TreeId> { self.inner.write_tree(path, contents).await } async fn read_commit(&self, id: &CommitId) -> BackendResult<Commit> { self.inner.read_commit(id).await } async fn write_commit( &self, contents: Commit, sign_with: Option<&mut SigningFn>, ) -> BackendResult<(CommitId, Commit)> { self.inner.write_commit(contents, sign_with).await } fn get_copy_records( &self, paths: Option<&[RepoPathBuf]>, root: &CommitId, head: &CommitId, ) -> BackendResult<BoxStream<'_, BackendResult<CopyRecord>>> { self.inner.get_copy_records(paths, root, head) } fn gc(&self, index: &dyn Index, keep_newer: SystemTime) -> BackendResult<()> { self.inner.gc(index, keep_newer) }}
// Copyright 2024 The Jujutsu Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Provides a backend for testing ACLs
use std::path::Path;
use std::pin::Pin;
use std::time::SystemTime;
use async_trait::async_trait;
use futures::stream::BoxStream;
use tokio::io::AsyncRead;
use crate::backend::Backend;
use crate::backend::BackendError;
use crate::backend::BackendLoadError;
use crate::backend::BackendResult;
use crate::backend::ChangeId;
use crate::backend::Commit;
use crate::backend::CommitId;
use crate::backend::CopyHistory;
use crate::backend::CopyId;
use crate::backend::CopyRecord;
use crate::backend::FileId;
use crate::backend::SigningFn;
use crate::backend::SymlinkId;
use crate::backend::Tree;
use crate::backend::TreeId;
use crate::git_backend::GitBackend;
use crate::index::Index;
use crate::object_id::ObjectId as _;
use crate::repo_path::RepoPath;
use crate::repo_path::RepoPathBuf;
use crate::settings::UserSettings;
const SECRET_CONTENTS_HEX: [&str; 2] = [
"d97c5eada5d8c52079031eef0107a4430a9617c5", // "secret\n"
"536aca34dbae6b2b8af26bebdcba83543c9546f0", // "secret"
];
/// A commit backend that's completely compatible with the Git backend, except
/// that it refuses to read files and symlinks with the word "secret" in the
/// path, or "secret" or "secret\n" in the content.
#[derive(Debug)]
pub struct SecretBackend {
inner: GitBackend,
}
impl SecretBackend {
/// "secret"
pub fn name() -> &'static str {
"secret"
}
/// Loads the backend from the given path.
pub fn load(settings: &UserSettings, store_path: &Path) -> Result<Self, BackendLoadError> {
let inner = GitBackend::load(settings, store_path)?;
Ok(Self { inner })
}
/// Convert a git repo to using `SecretBackend`
// TODO: Avoid this hack
pub fn adopt_git_repo(workspace_path: &Path) {
std::fs::write(
workspace_path
.join(".jj")
.join("repo")
.join("store")
.join("type"),
Self::name(),
)
.unwrap();
}
}
#[async_trait]
impl Backend for SecretBackend {
fn name(&self) -> &str {
Self::name()
}
fn commit_id_length(&self) -> usize {
self.inner.commit_id_length()
}
fn change_id_length(&self) -> usize {
self.inner.change_id_length()
}
fn root_commit_id(&self) -> &CommitId {
self.inner.root_commit_id()
}
fn root_change_id(&self) -> &ChangeId {
self.inner.root_change_id()
}
fn empty_tree_id(&self) -> &TreeId {
self.inner.empty_tree_id()
}
fn concurrency(&self) -> usize {
1
}
async fn read_file(
&self,
path: &RepoPath,
id: &FileId,
) -> BackendResult<Pin<Box<dyn AsyncRead + Send>>> {
if path.as_internal_file_string().contains("secret")
|| SECRET_CONTENTS_HEX.contains(&id.hex().as_ref())
{
return Err(BackendError::ReadAccessDenied {
object_type: "file".to_string(),
hash: id.hex(),
source: "No access".into(),
});
}
self.inner.read_file(path, id).await
}
async fn write_file(
&self,
path: &RepoPath,
contents: &mut (dyn AsyncRead + Send + Unpin),
) -> BackendResult<FileId> {
self.inner.write_file(path, contents).await
}
async fn read_symlink(&self, path: &RepoPath, id: &SymlinkId) -> BackendResult<String> {
if path.as_internal_file_string().contains("secret")
|| SECRET_CONTENTS_HEX.contains(&id.hex().as_ref())
{
return Err(BackendError::ReadAccessDenied {
object_type: "symlink".to_string(),
hash: id.hex(),
source: "No access".into(),
});
}
self.inner.read_symlink(path, id).await
}
async fn write_symlink(&self, path: &RepoPath, target: &str) -> BackendResult<SymlinkId> {
self.inner.write_symlink(path, target).await
}
async fn read_copy(&self, _id: &CopyId) -> BackendResult<CopyHistory> {
Err(BackendError::Unsupported(
"The secret backend doesn't support copies".to_string(),
))
}
async fn write_copy(&self, _contents: &CopyHistory) -> BackendResult<CopyId> {
Err(BackendError::Unsupported(
"The secret backend doesn't support copies".to_string(),
))
}
async fn get_related_copies(&self, _copy_id: &CopyId) -> BackendResult<Vec<CopyHistory>> {
Err(BackendError::Unsupported(
"The secret backend doesn't support copies".to_string(),
))
}
async fn read_tree(&self, path: &RepoPath, id: &TreeId) -> BackendResult<Tree> {
self.inner.read_tree(path, id).await
}
async fn write_tree(&self, path: &RepoPath, contents: &Tree) -> BackendResult<TreeId> {
self.inner.write_tree(path, contents).await
}
async fn read_commit(&self, id: &CommitId) -> BackendResult<Commit> {
self.inner.read_commit(id).await
}
async fn write_commit(
&self,
contents: Commit,
sign_with: Option<&mut SigningFn>,
) -> BackendResult<(CommitId, Commit)> {
self.inner.write_commit(contents, sign_with).await
}
fn get_copy_records(
&self,
paths: Option<&[RepoPathBuf]>,
root: &CommitId,
head: &CommitId,
) -> BackendResult<BoxStream<'_, BackendResult<CopyRecord>>> {
self.inner.get_copy_records(paths, root, head)
}
fn gc(&self, index: &dyn Index, keep_newer: SystemTime) -> BackendResult<()> {
self.inner.gc(index, keep_newer)
}
}