mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 02:22:21 +00:00
dav: Add interface for copy and move
This commit is contained in:
@@ -28,6 +28,9 @@ pub enum Error {
|
||||
|
||||
#[error("Precondition Failed")]
|
||||
PreconditionFailed,
|
||||
|
||||
#[error("Forbidden")]
|
||||
Forbidden,
|
||||
}
|
||||
|
||||
impl Error {
|
||||
@@ -49,6 +52,7 @@ impl Error {
|
||||
Error::PropReadOnly => StatusCode::CONFLICT,
|
||||
Error::PreconditionFailed => StatusCode::PRECONDITION_FAILED,
|
||||
Self::IOError(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||
Self::Forbidden => StatusCode::FORBIDDEN,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,51 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
header::{Depth, Overwrite},
|
||||
resource::ResourceService,
|
||||
};
|
||||
use axum::{
|
||||
extract::{MatchedPath, Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit_serde::ParamsDeserializer;
|
||||
use serde::Deserialize;
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(_path, _resource_service,))]
|
||||
#[instrument(skip(path, resource_service,))]
|
||||
pub(crate) async fn axum_route_copy<R: ResourceService>(
|
||||
Path(_path): Path<R::PathComponents>,
|
||||
State(_resource_service): State<R>,
|
||||
Path(path): Path<R::PathComponents>,
|
||||
State(resource_service): State<R>,
|
||||
depth: Option<Depth>,
|
||||
principal: R::Principal,
|
||||
overwrite: Overwrite,
|
||||
matched_path: MatchedPath,
|
||||
header_map: HeaderMap,
|
||||
) -> Result<Response, R::Error> {
|
||||
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
|
||||
// can return a 403 error
|
||||
let _depth = depth.unwrap_or(Depth::Infinity);
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
let destination = header_map
|
||||
.get("Destination")
|
||||
.ok_or(crate::Error::Forbidden)?
|
||||
.to_str()
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
|
||||
let mut router = matchit::Router::new();
|
||||
router.insert(matched_path.as_str(), ()).unwrap();
|
||||
if let Ok(matchit::Match { params, .. }) = router.at(destination) {
|
||||
let params: Vec<(&str, &str)> = params.iter().collect();
|
||||
let params = matchit_serde::Params(¶ms);
|
||||
let dest_path = R::PathComponents::deserialize(&ParamsDeserializer::new(params))
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
|
||||
if resource_service
|
||||
.copy_resource(&path, &dest_path, &principal, overwrite.is_true())
|
||||
.await?
|
||||
{
|
||||
// Overwritten
|
||||
Ok(StatusCode::NO_CONTENT.into_response())
|
||||
} else {
|
||||
// Not overwritten
|
||||
Ok(StatusCode::CREATED.into_response())
|
||||
}
|
||||
} else {
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,25 +1,51 @@
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::StatusCode;
|
||||
use tracing::instrument;
|
||||
|
||||
use crate::{
|
||||
header::{Depth, Overwrite},
|
||||
resource::ResourceService,
|
||||
};
|
||||
use axum::{
|
||||
extract::{MatchedPath, Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
};
|
||||
use http::{HeaderMap, StatusCode};
|
||||
use matchit_serde::ParamsDeserializer;
|
||||
use serde::Deserialize;
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(_path, _resource_service,))]
|
||||
#[instrument(skip(path, resource_service,))]
|
||||
pub(crate) async fn axum_route_move<R: ResourceService>(
|
||||
Path(_path): Path<R::PathComponents>,
|
||||
State(_resource_service): State<R>,
|
||||
Path(path): Path<R::PathComponents>,
|
||||
State(resource_service): State<R>,
|
||||
depth: Option<Depth>,
|
||||
principal: R::Principal,
|
||||
overwrite: Overwrite,
|
||||
matched_path: MatchedPath,
|
||||
header_map: HeaderMap,
|
||||
) -> Result<Response, R::Error> {
|
||||
// TODO: Actually implement, but to be WebDAV-compliant we must at least support this route but
|
||||
// can return a 403 error
|
||||
let _depth = depth.unwrap_or(Depth::Infinity);
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
let destination = header_map
|
||||
.get("Destination")
|
||||
.ok_or(crate::Error::Forbidden)?
|
||||
.to_str()
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
|
||||
let mut router = matchit::Router::new();
|
||||
router.insert(matched_path.as_str(), ()).unwrap();
|
||||
if let Ok(matchit::Match { params, .. }) = router.at(destination) {
|
||||
let params: Vec<(&str, &str)> = params.iter().collect();
|
||||
let params = matchit_serde::Params(¶ms);
|
||||
let dest_path = R::PathComponents::deserialize(&ParamsDeserializer::new(params))
|
||||
.map_err(|_| crate::Error::Forbidden)?;
|
||||
|
||||
if resource_service
|
||||
.copy_resource(&path, &dest_path, &principal, overwrite.is_true())
|
||||
.await?
|
||||
{
|
||||
// Overwritten
|
||||
Ok(StatusCode::NO_CONTENT.into_response())
|
||||
} else {
|
||||
// Not overwritten
|
||||
Ok(StatusCode::CREATED.into_response())
|
||||
}
|
||||
} else {
|
||||
Ok(StatusCode::FORBIDDEN.into_response())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,13 @@ use serde::Deserialize;
|
||||
|
||||
#[async_trait]
|
||||
pub trait ResourceService: Clone + Sized + Send + Sync + AxumMethods + 'static {
|
||||
type PathComponents: for<'de> Deserialize<'de> + Sized + Send + Sync + Clone + 'static; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
|
||||
type PathComponents: std::fmt::Debug
|
||||
+ for<'de> Deserialize<'de>
|
||||
+ Sized
|
||||
+ Send
|
||||
+ Sync
|
||||
+ Clone
|
||||
+ 'static; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
|
||||
type MemberType: Resource<Error = Self::Error, Principal = Self::Principal>
|
||||
+ super::ResourceName;
|
||||
type Resource: Resource<Error = Self::Error, Principal = Self::Principal>;
|
||||
@@ -47,6 +53,28 @@ pub trait ResourceService: Clone + Sized + Send + Sync + AxumMethods + 'static {
|
||||
Err(crate::Error::Unauthorized.into())
|
||||
}
|
||||
|
||||
// Returns whether an existing resource was overwritten
|
||||
async fn copy_resource(
|
||||
&self,
|
||||
_path: &Self::PathComponents,
|
||||
_destination: &Self::PathComponents,
|
||||
_user: &Self::Principal,
|
||||
_overwrite: bool,
|
||||
) -> Result<bool, Self::Error> {
|
||||
Err(crate::Error::Forbidden.into())
|
||||
}
|
||||
|
||||
// Returns whether an existing resource was overwritten
|
||||
async fn move_resource(
|
||||
&self,
|
||||
_path: &Self::PathComponents,
|
||||
_destination: &Self::PathComponents,
|
||||
_user: &Self::Principal,
|
||||
_overwrite: bool,
|
||||
) -> Result<bool, Self::Error> {
|
||||
Err(crate::Error::Forbidden.into())
|
||||
}
|
||||
|
||||
fn axum_service(self) -> AxumService<Self>
|
||||
where
|
||||
Self: AxumMethods,
|
||||
|
||||
Reference in New Issue
Block a user