mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-14 05:52:19 +00:00
outsource root resource to dav crate
This commit is contained in:
@@ -5,9 +5,9 @@ use actix_web::web::{self, Data};
|
|||||||
use calendar::resource::CalendarResourceService;
|
use calendar::resource::CalendarResourceService;
|
||||||
use calendar_object::resource::CalendarObjectResourceService;
|
use calendar_object::resource::CalendarObjectResourceService;
|
||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
use principal::PrincipalResourceService;
|
use principal::{PrincipalResource, PrincipalResourceService};
|
||||||
use root::RootResourceService;
|
|
||||||
use rustical_dav::resource::ResourceService;
|
use rustical_dav::resource::ResourceService;
|
||||||
|
use rustical_dav::resources::RootResourceService;
|
||||||
use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider};
|
use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider};
|
||||||
use rustical_store::CalendarStore;
|
use rustical_store::CalendarStore;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -16,7 +16,6 @@ pub mod calendar;
|
|||||||
pub mod calendar_object;
|
pub mod calendar_object;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod principal;
|
pub mod principal;
|
||||||
pub mod root;
|
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|
||||||
@@ -55,7 +54,7 @@ pub fn configure_dav<AP: AuthenticationProvider, C: CalendarStore + ?Sized>(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.app_data(Data::from(store.clone()))
|
.app_data(Data::from(store.clone()))
|
||||||
.service(RootResourceService::actix_resource())
|
.service(RootResourceService::<PrincipalResource>::actix_resource())
|
||||||
.service(
|
.service(
|
||||||
web::scope("/user").service(
|
web::scope("/user").service(
|
||||||
web::scope("/{principal}")
|
web::scope("/{principal}")
|
||||||
|
|||||||
@@ -1,110 +0,0 @@
|
|||||||
use crate::principal::PrincipalResource;
|
|
||||||
use crate::Error;
|
|
||||||
use actix_web::dev::ResourceMap;
|
|
||||||
use actix_web::HttpRequest;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use derive_more::derive::{From, TryInto};
|
|
||||||
use rustical_dav::extension::BoxedExtension;
|
|
||||||
use rustical_dav::extensions::{
|
|
||||||
CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName,
|
|
||||||
};
|
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
|
||||||
use rustical_dav::resource::{InvalidProperty, Resource, ResourceService};
|
|
||||||
use rustical_store::auth::User;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use strum::{EnumString, VariantNames};
|
|
||||||
|
|
||||||
#[derive(EnumString, VariantNames, Clone, From, TryInto)]
|
|
||||||
#[strum(serialize_all = "kebab-case")]
|
|
||||||
pub enum RootPropName {
|
|
||||||
Resourcetype,
|
|
||||||
#[from]
|
|
||||||
#[try_into]
|
|
||||||
#[strum(disabled)]
|
|
||||||
ExtCommonProperties(CommonPropertiesPropName),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Default, Debug)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct Resourcetype {
|
|
||||||
collection: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, From, TryInto)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum RootProp {
|
|
||||||
// WebDAV (RFC 2518)
|
|
||||||
Resourcetype(Resourcetype),
|
|
||||||
|
|
||||||
#[serde(skip_deserializing, untagged)]
|
|
||||||
#[from]
|
|
||||||
#[try_into]
|
|
||||||
ExtCommonProperties(CommonPropertiesProp<RootResource>),
|
|
||||||
|
|
||||||
#[serde(untagged)]
|
|
||||||
Invalid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InvalidProperty for RootProp {
|
|
||||||
fn invalid_property(&self) -> bool {
|
|
||||||
matches!(self, Self::Invalid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RootResource;
|
|
||||||
|
|
||||||
impl Resource for RootResource {
|
|
||||||
type PropName = RootPropName;
|
|
||||||
type Prop = RootProp;
|
|
||||||
type Error = Error;
|
|
||||||
type ResourceType = Resourcetype;
|
|
||||||
|
|
||||||
fn list_extensions() -> Vec<BoxedExtension<Self>> {
|
|
||||||
vec![BoxedExtension::from_ext(CommonPropertiesExtension::<
|
|
||||||
PrincipalResource,
|
|
||||||
>::default())]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_prop(
|
|
||||||
&self,
|
|
||||||
_rmap: &ResourceMap,
|
|
||||||
_user: &User,
|
|
||||||
prop: &Self::PropName,
|
|
||||||
) -> Result<Self::Prop, Self::Error> {
|
|
||||||
Ok(match prop {
|
|
||||||
RootPropName::Resourcetype => RootProp::Resourcetype(Resourcetype::default()),
|
|
||||||
_ => panic!("we shouldn't end up here"),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn resource_name() -> &'static str {
|
|
||||||
"caldav_root"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_user_privileges(&self, _user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
|
||||||
Ok(UserPrivilegeSet::all())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RootResourceService;
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl ResourceService for RootResourceService {
|
|
||||||
type PathComponents = ();
|
|
||||||
type MemberType = RootResource;
|
|
||||||
type Resource = RootResource;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
async fn new(
|
|
||||||
_req: &HttpRequest,
|
|
||||||
_path_components: Self::PathComponents,
|
|
||||||
) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_resource(&self) -> Result<Self::Resource, Self::Error> {
|
|
||||||
Ok(RootResource)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,9 +10,9 @@ use address_object::resource::AddressObjectResourceService;
|
|||||||
use addressbook::resource::AddressbookResourceService;
|
use addressbook::resource::AddressbookResourceService;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
use futures_util::FutureExt;
|
use futures_util::FutureExt;
|
||||||
use principal::PrincipalResourceService;
|
use principal::{PrincipalResource, PrincipalResourceService};
|
||||||
use root::RootResourceService;
|
|
||||||
use rustical_dav::resource::ResourceService;
|
use rustical_dav::resource::ResourceService;
|
||||||
|
use rustical_dav::resources::RootResourceService;
|
||||||
use rustical_store::{
|
use rustical_store::{
|
||||||
auth::{AuthenticationMiddleware, AuthenticationProvider},
|
auth::{AuthenticationMiddleware, AuthenticationProvider},
|
||||||
AddressbookStore,
|
AddressbookStore,
|
||||||
@@ -23,7 +23,6 @@ pub mod address_object;
|
|||||||
pub mod addressbook;
|
pub mod addressbook;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
pub mod principal;
|
pub mod principal;
|
||||||
pub mod root;
|
|
||||||
|
|
||||||
pub fn configure_well_known(cfg: &mut web::ServiceConfig, carddav_root: String) {
|
pub fn configure_well_known(cfg: &mut web::ServiceConfig, carddav_root: String) {
|
||||||
cfg.service(web::redirect("/carddav", carddav_root).permanent());
|
cfg.service(web::redirect("/carddav", carddav_root).permanent());
|
||||||
@@ -60,7 +59,7 @@ pub fn configure_dav<AP: AuthenticationProvider, A: AddressbookStore + ?Sized>(
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
.app_data(Data::from(store.clone()))
|
.app_data(Data::from(store.clone()))
|
||||||
.service(RootResourceService::actix_resource())
|
.service(RootResourceService::<PrincipalResource>::actix_resource())
|
||||||
.service(
|
.service(
|
||||||
web::scope("/user").service(
|
web::scope("/user").service(
|
||||||
web::scope("/{principal}")
|
web::scope("/{principal}")
|
||||||
|
|||||||
@@ -1,103 +0,0 @@
|
|||||||
use crate::principal::PrincipalResource;
|
|
||||||
use crate::Error;
|
|
||||||
use actix_web::dev::ResourceMap;
|
|
||||||
use actix_web::HttpRequest;
|
|
||||||
use async_trait::async_trait;
|
|
||||||
use derive_more::{From, TryInto};
|
|
||||||
use rustical_dav::extension::BoxedExtension;
|
|
||||||
use rustical_dav::extensions::{
|
|
||||||
CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName,
|
|
||||||
};
|
|
||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
|
||||||
use rustical_dav::resource::{InvalidProperty, Resource, ResourceService};
|
|
||||||
use rustical_store::auth::User;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use strum::{EnumString, VariantNames};
|
|
||||||
|
|
||||||
#[derive(EnumString, VariantNames, Clone, From, TryInto)]
|
|
||||||
#[strum(serialize_all = "kebab-case")]
|
|
||||||
pub enum RootPropName {
|
|
||||||
#[from]
|
|
||||||
#[try_into]
|
|
||||||
#[strum(disabled)]
|
|
||||||
ExtCommonProperties(CommonPropertiesPropName),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, Default, Debug)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub struct Resourcetype {
|
|
||||||
collection: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Deserialize, Serialize, From, TryInto)]
|
|
||||||
#[serde(rename_all = "kebab-case")]
|
|
||||||
pub enum RootProp {
|
|
||||||
#[serde(skip_deserializing, untagged)]
|
|
||||||
#[from]
|
|
||||||
#[try_into]
|
|
||||||
ExtCommonProperties(CommonPropertiesProp<RootResource>),
|
|
||||||
|
|
||||||
#[serde(untagged)]
|
|
||||||
Invalid,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InvalidProperty for RootProp {
|
|
||||||
fn invalid_property(&self) -> bool {
|
|
||||||
matches!(self, Self::Invalid)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct RootResource;
|
|
||||||
|
|
||||||
impl Resource for RootResource {
|
|
||||||
type PropName = RootPropName;
|
|
||||||
type Prop = RootProp;
|
|
||||||
type Error = Error;
|
|
||||||
type ResourceType = Resourcetype;
|
|
||||||
|
|
||||||
fn list_extensions() -> Vec<BoxedExtension<Self>> {
|
|
||||||
vec![BoxedExtension::from_ext(CommonPropertiesExtension::<
|
|
||||||
PrincipalResource,
|
|
||||||
>::default())]
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_prop(
|
|
||||||
&self,
|
|
||||||
_rmap: &ResourceMap,
|
|
||||||
_user: &User,
|
|
||||||
_prop: &Self::PropName,
|
|
||||||
) -> Result<Self::Prop, Self::Error> {
|
|
||||||
panic!("we shouldn't end up here")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn resource_name() -> &'static str {
|
|
||||||
"carddav_root"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_user_privileges(&self, _user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
|
||||||
Ok(UserPrivilegeSet::all())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct RootResourceService;
|
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
|
||||||
impl ResourceService for RootResourceService {
|
|
||||||
type PathComponents = ();
|
|
||||||
type MemberType = PrincipalResource;
|
|
||||||
type Resource = RootResource;
|
|
||||||
type Error = Error;
|
|
||||||
|
|
||||||
async fn new(
|
|
||||||
_req: &HttpRequest,
|
|
||||||
_path_components: Self::PathComponents,
|
|
||||||
) -> Result<Self, Self::Error> {
|
|
||||||
Ok(Self)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn get_resource(&self) -> Result<Self::Resource, Self::Error> {
|
|
||||||
Ok(RootResource)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ pub mod methods;
|
|||||||
pub mod namespace;
|
pub mod namespace;
|
||||||
pub mod privileges;
|
pub mod privileges;
|
||||||
pub mod resource;
|
pub mod resource;
|
||||||
|
pub mod resources;
|
||||||
pub mod xml;
|
pub mod xml;
|
||||||
|
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ impl<T: ResourceReadProp + for<'de> Deserialize<'de>> ResourceProp for T {}
|
|||||||
pub trait ResourcePropName: FromStr + VariantNames {}
|
pub trait ResourcePropName: FromStr + VariantNames {}
|
||||||
impl<T: FromStr + VariantNames> ResourcePropName for T {}
|
impl<T: FromStr + VariantNames> ResourcePropName for T {}
|
||||||
|
|
||||||
pub trait Resource: Clone {
|
pub trait Resource: Clone + 'static {
|
||||||
type PropName: ResourcePropName;
|
type PropName: ResourcePropName;
|
||||||
type Prop: ResourceProp;
|
type Prop: ResourceProp;
|
||||||
type Error: ResponseError + From<crate::Error>;
|
type Error: ResponseError + From<crate::Error>;
|
||||||
@@ -132,6 +132,14 @@ pub trait Resource: Clone {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut prop_responses = Vec::new();
|
||||||
|
|
||||||
|
for extension in Self::list_extensions() {
|
||||||
|
let (ext_invalid_props, ext_responses) = extension.propfind(self, props, user, rmap)?;
|
||||||
|
props = ext_invalid_props;
|
||||||
|
prop_responses.extend(ext_responses);
|
||||||
|
}
|
||||||
|
|
||||||
let (valid_props, invalid_props): (Vec<Option<Self::PropName>>, Vec<Option<&str>>) = props
|
let (valid_props, invalid_props): (Vec<Option<Self::PropName>>, Vec<Option<&str>>) = props
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|prop| {
|
.map(|prop| {
|
||||||
@@ -143,19 +151,14 @@ pub trait Resource: Clone {
|
|||||||
})
|
})
|
||||||
.unzip();
|
.unzip();
|
||||||
let valid_props: Vec<Self::PropName> = valid_props.into_iter().flatten().collect();
|
let valid_props: Vec<Self::PropName> = valid_props.into_iter().flatten().collect();
|
||||||
let mut invalid_props: Vec<&str> = invalid_props.into_iter().flatten().collect();
|
let invalid_props: Vec<&str> = invalid_props.into_iter().flatten().collect();
|
||||||
|
|
||||||
let mut prop_responses = valid_props
|
prop_responses.extend(
|
||||||
|
valid_props
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|prop| self.get_prop(rmap, user, &prop))
|
.map(|prop| self.get_prop(rmap, user, &prop))
|
||||||
.collect::<Result<Vec<Self::Prop>, Self::Error>>()?;
|
.collect::<Result<Vec<Self::Prop>, Self::Error>>()?,
|
||||||
|
);
|
||||||
for extension in Self::list_extensions() {
|
|
||||||
let (ext_invalid_props, ext_responses) =
|
|
||||||
extension.propfind(self, invalid_props, user, rmap)?;
|
|
||||||
invalid_props = ext_invalid_props;
|
|
||||||
prop_responses.extend(ext_responses);
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut propstats = vec![PropstatWrapper::Normal(PropstatElement {
|
let mut propstats = vec![PropstatWrapper::Normal(PropstatElement {
|
||||||
status: format!("HTTP/1.1 {}", StatusCode::OK),
|
status: format!("HTTP/1.1 {}", StatusCode::OK),
|
||||||
|
|||||||
3
crates/dav/src/resources/mod.rs
Normal file
3
crates/dav/src/resources/mod.rs
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
pub mod root;
|
||||||
|
|
||||||
|
pub use root::{RootResource, RootResourceService};
|
||||||
82
crates/dav/src/resources/root.rs
Normal file
82
crates/dav/src/resources/root.rs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
use std::any::type_name;
|
||||||
|
use std::marker::PhantomData;
|
||||||
|
|
||||||
|
use crate::extension::BoxedExtension;
|
||||||
|
use crate::extensions::{
|
||||||
|
CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName,
|
||||||
|
};
|
||||||
|
use crate::privileges::UserPrivilegeSet;
|
||||||
|
use crate::resource::{Resource, ResourceService};
|
||||||
|
use actix_web::dev::ResourceMap;
|
||||||
|
use actix_web::HttpRequest;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use rustical_store::auth::User;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Deserialize, Serialize, Default, Debug)]
|
||||||
|
#[serde(rename_all = "kebab-case")]
|
||||||
|
pub struct Resourcetype {
|
||||||
|
collection: (),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct RootResource<PR: Resource>(PhantomData<PR>);
|
||||||
|
|
||||||
|
impl<PR: Resource> Default for RootResource<PR> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self(Default::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<PR: Resource> Resource for RootResource<PR> {
|
||||||
|
type PropName = CommonPropertiesPropName;
|
||||||
|
type Prop = CommonPropertiesProp<RootResource<PR>>;
|
||||||
|
type Error = PR::Error;
|
||||||
|
type ResourceType = Resourcetype;
|
||||||
|
|
||||||
|
fn list_extensions() -> Vec<BoxedExtension<Self>> {
|
||||||
|
vec![BoxedExtension::from_ext(
|
||||||
|
CommonPropertiesExtension::<PR>::default(),
|
||||||
|
)]
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_prop(
|
||||||
|
&self,
|
||||||
|
_rmap: &ResourceMap,
|
||||||
|
_user: &User,
|
||||||
|
_prop: &Self::PropName,
|
||||||
|
) -> Result<Self::Prop, Self::Error> {
|
||||||
|
panic!("we shouldn't end up here")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn resource_name() -> &'static str {
|
||||||
|
type_name::<Self>()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_user_privileges(&self, _user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||||
|
Ok(UserPrivilegeSet::all())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct RootResourceService<PR: Resource>(PhantomData<PR>);
|
||||||
|
|
||||||
|
#[async_trait(?Send)]
|
||||||
|
impl<PR: Resource> ResourceService for RootResourceService<PR> {
|
||||||
|
type PathComponents = ();
|
||||||
|
type MemberType = PR;
|
||||||
|
type Resource = RootResource<PR>;
|
||||||
|
type Error = PR::Error;
|
||||||
|
|
||||||
|
async fn new(
|
||||||
|
_req: &HttpRequest,
|
||||||
|
_path_components: Self::PathComponents,
|
||||||
|
) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Self(Default::default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn get_resource(&self) -> Result<Self::Resource, Self::Error> {
|
||||||
|
Ok(RootResource::<PR>::default())
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user