P1: WebDAV ACL enforcement (RFC 3744)
Some checks failed
Test / build (push) Has been cancelled
Test / test (push) Has been cancelled

- Add enable_acl field to VfsDavFs
- Add check_acl() helper method
- ACL checks in open(), read_dir(), create_dir(), remove_dir(), remove_file(), rename()
- Uses VfsAceMask for permission checks (ReadData, WriteData, etc.)
- Returns FsError::Forbidden if ACL denies access

Tests: 289 passed, 0 failed
This commit is contained in:
Warren
2026-06-21 18:37:48 +08:00
parent 2a0376cc58
commit 9c82830959

View File

@@ -81,6 +81,7 @@ pub struct VfsDavFs {
versioning: Option<Arc<WebDavVersioning>>,
props_data: Arc<RwLock<HashMap<String, Vec<DavProp>>>>,
props_path: PathBuf,
enable_acl: bool,
}
impl Clone for VfsDavFs {
@@ -93,6 +94,7 @@ impl Clone for VfsDavFs {
versioning: self.versioning.clone(),
props_data: self.props_data.clone(),
props_path: self.props_path.clone(),
enable_acl: self.enable_acl,
}
}
}
@@ -178,6 +180,7 @@ impl VfsDavFs {
versioning: None,
props_data,
props_path,
enable_acl: true,
})
}
@@ -198,9 +201,14 @@ impl VfsDavFs {
versioning: Some(versioning),
props_data,
props_path,
enable_acl: true,
})
}
pub fn set_enable_acl(&mut self, enable: bool) {
self.enable_acl = enable;
}
fn rel_key(&self, path: &DavPath) -> String {
let rel = path.as_pathbuf();
rel.to_string_lossy().to_string()
@@ -617,6 +625,18 @@ impl VfsDavFs {
xml: Some(Self::empty_acl_xml()),
}
}
fn check_acl(&self, path: &Path, mask: crate::vfs::VfsAceMask) -> Result<(), FsError> {
if !self.enable_acl {
return Ok(());
}
match self.vfs.check_acl(path, &self.user_uuid, mask) {
Ok(true) => Ok(()),
Ok(false) => Err(FsError::Forbidden),
Err(crate::vfs::VfsError::Unsupported(_)) => Ok(()),
Err(_) => Err(FsError::Forbidden),
}
}
}
impl DavFileSystem for VfsDavFs {
@@ -631,6 +651,9 @@ impl DavFileSystem for VfsDavFs {
};
if options.write {
if let Err(e) = self.check_acl(&full_path, crate::vfs::VfsAceMask::WriteData) {
return Box::pin(std::future::ready(Err(e)));
}
let file = VfsDavFile::new_write(
full_path,
self.vfs.clone_boxed(),
@@ -640,6 +663,9 @@ impl DavFileSystem for VfsDavFs {
);
Box::pin(std::future::ready(Ok(Box::new(file) as Box<dyn DavFile>)))
} else {
if let Err(e) = self.check_acl(&full_path, crate::vfs::VfsAceMask::ReadData) {
return Box::pin(std::future::ready(Err(e)));
}
let flags = OpenFlags::new().read();
match self.vfs.open_file(&full_path, &flags) {
Ok(vfs_file) => {
@@ -661,6 +687,10 @@ impl DavFileSystem for VfsDavFs {
Err(e) => return Box::pin(std::future::ready(Err(e))),
};
if let Err(e) = self.check_acl(&full_path, crate::vfs::VfsAceMask::ListDirectory) {
return Box::pin(std::future::ready(Err(e)));
}
match self.vfs.read_dir(&full_path) {
Ok(entries) => {
let results: Vec<Box<dyn DavDirEntry>> = entries
@@ -698,6 +728,9 @@ impl DavFileSystem for VfsDavFs {
Ok(p) => p,
Err(e) => return Box::pin(std::future::ready(Err(e))),
};
if let Err(e) = self.check_acl(&full_path, crate::vfs::VfsAceMask::AddSubdirectory) {
return Box::pin(std::future::ready(Err(e)));
}
if self.vfs.exists(&full_path) {
return Box::pin(std::future::ready(Err(FsError::Exists)));
}
@@ -712,6 +745,9 @@ impl DavFileSystem for VfsDavFs {
Ok(p) => p,
Err(e) => return Box::pin(std::future::ready(Err(e))),
};
if let Err(e) = self.check_acl(&full_path, crate::vfs::VfsAceMask::DeleteChild) {
return Box::pin(std::future::ready(Err(e)));
}
match self.vfs.remove_dir_all(&full_path) {
Ok(_) => Box::pin(std::future::ready(Ok(()))),
Err(e) => Box::pin(std::future::ready(Err(map_vfs_error(e)))),
@@ -723,6 +759,9 @@ impl DavFileSystem for VfsDavFs {
Ok(p) => p,
Err(e) => return Box::pin(std::future::ready(Err(e))),
};
if let Err(e) = self.check_acl(&full_path, crate::vfs::VfsAceMask::Delete) {
return Box::pin(std::future::ready(Err(e)));
}
match self.vfs.remove_file(&full_path) {
Ok(_) => Box::pin(std::future::ready(Ok(()))),
Err(e) => Box::pin(std::future::ready(Err(map_vfs_error(e)))),
@@ -738,6 +777,12 @@ impl DavFileSystem for VfsDavFs {
Ok(p) => p,
Err(e) => return Box::pin(std::future::ready(Err(e))),
};
if let Err(e) = self.check_acl(&from_path, crate::vfs::VfsAceMask::Delete) {
return Box::pin(std::future::ready(Err(e)));
}
if let Err(e) = self.check_acl(&to_path, crate::vfs::VfsAceMask::AddFile) {
return Box::pin(std::future::ready(Err(e)));
}
let from_key = self.rel_key(from);
let to_key = self.rel_key(to);
let props_data = self.props_data.clone();