209 lines · 6.1 KB · Rust
RawDownload
// 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)    }}