mirror of
https://github.com/martinvonz/jj.git
synced 2025-02-08 05:30:25 +00:00
This doesn't provide any benefit yet bit I think we've known for a while that we want to make the backend write methods async. It's just not been important to Google because we have the local daemon process that makes our writes pretty fast. Regardless, this first commit just changes the API and all callers immediately block for now, so it won't help even on slow backends.
199 lines
5.8 KiB
Rust
199 lines
5.8 KiB
Rust
// 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::any::Any;
|
|
use std::io::Read;
|
|
use std::path::Path;
|
|
use std::time::SystemTime;
|
|
|
|
use async_trait::async_trait;
|
|
use futures::stream::BoxStream;
|
|
|
|
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::Conflict;
|
|
use crate::backend::ConflictId;
|
|
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;
|
|
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(SecretBackend { 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 as_any(&self) -> &dyn Any {
|
|
self
|
|
}
|
|
|
|
fn name(&self) -> &str {
|
|
SecretBackend::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<Box<dyn Read>> {
|
|
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 Read + Send),
|
|
) -> 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_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
|
|
}
|
|
|
|
fn read_conflict(&self, path: &RepoPath, id: &ConflictId) -> BackendResult<Conflict> {
|
|
self.inner.read_conflict(path, id)
|
|
}
|
|
|
|
fn write_conflict(&self, path: &RepoPath, contents: &Conflict) -> BackendResult<ConflictId> {
|
|
self.inner.write_conflict(path, contents)
|
|
}
|
|
|
|
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)
|
|
}
|
|
}
|