mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 21:42:34 +00:00
Move properties into separate files
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
use crate::Error;
|
||||
use crate::calendar::CalendarResourceService;
|
||||
use crate::calendar::prop::SupportedCalendarComponentSet;
|
||||
use crate::calendar::resource::CalendarResourceService;
|
||||
use axum::extract::{Path, State};
|
||||
use axum::response::{IntoResponse, Response};
|
||||
use http::StatusCode;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
|
||||
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
|
||||
use rustical_dav::xml::PropfindType;
|
||||
use rustical_ical::CalendarObject;
|
||||
use rustical_store::CalendarStore;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
|
||||
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
|
||||
use rustical_dav::xml::PropfindType;
|
||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
CalDavPrincipalUri, Error,
|
||||
calendar::resource::CalendarResourceService,
|
||||
calendar_object::resource::{
|
||||
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
|
||||
calendar::CalendarResourceService,
|
||||
calendar_object::{
|
||||
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, resource::CalendarObjectResource,
|
||||
},
|
||||
};
|
||||
use axum::{
|
||||
@@ -147,7 +147,7 @@ pub async fn route_report_calendar<C: CalendarStore, S: SubscriptionStore>(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::calendar_object::resource::{CalendarData, CalendarObjectPropName, ExpandElement};
|
||||
use crate::calendar_object::{CalendarData, CalendarObjectPropName, ExpandElement};
|
||||
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
|
||||
use rustical_dav::xml::PropElement;
|
||||
use rustical_ical::UtcDateTime;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
Error,
|
||||
calendar_object::resource::{
|
||||
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
|
||||
calendar_object::{
|
||||
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, resource::CalendarObjectResource,
|
||||
},
|
||||
};
|
||||
use http::StatusCode;
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
pub mod methods;
|
||||
pub mod prop;
|
||||
pub mod resource;
|
||||
mod service;
|
||||
|
||||
pub use service::CalendarResourceService;
|
||||
|
||||
@@ -1,32 +1,20 @@
|
||||
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
|
||||
use crate::calendar::methods::mkcalendar::route_mkcalendar;
|
||||
use crate::calendar::methods::report::route_report_calendar;
|
||||
use crate::calendar_object::resource::{CalendarObjectResource, CalendarObjectResourceService};
|
||||
use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use axum::extract::Request;
|
||||
use axum::handler::Handler;
|
||||
use axum::response::Response;
|
||||
use crate::Error;
|
||||
use chrono::{DateTime, Utc};
|
||||
use derive_more::derive::{From, Into};
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::extensions::{
|
||||
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
||||
};
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
||||
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||
use rustical_dav_push::DavPushExtension;
|
||||
use rustical_ical::CalDateTime;
|
||||
use rustical_store::Calendar;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
||||
use rustical_xml::{EnumVariants, PropName};
|
||||
use rustical_xml::{XmlDeserialize, XmlSerialize};
|
||||
use std::convert::Infallible;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tower::Service;
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "CalendarPropName")]
|
||||
@@ -317,113 +305,3 @@ impl Resource for CalendarResource {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CalendarResourceService<C: CalendarStore, S: SubscriptionStore> {
|
||||
pub(crate) cal_store: Arc<C>,
|
||||
pub(crate) sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarResourceService<C, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
cal_store: self.cal_store.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> CalendarResourceService<C, S> {
|
||||
pub fn new(cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
cal_store,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourceService<C, S> {
|
||||
type MemberType = CalendarObjectResource;
|
||||
type PathComponents = (String, String); // principal, calendar_id
|
||||
type Resource = CalendarResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Error> {
|
||||
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
|
||||
Ok(CalendarResource {
|
||||
cal: calendar,
|
||||
read_only: self.cal_store.is_read_only(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
Ok(self
|
||||
.cal_store
|
||||
.get_objects(principal, cal_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|object| CalendarObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn save_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
file: Self::Resource,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.cal_store
|
||||
.update_calendar(principal.to_owned(), cal_id.to_owned(), file.into())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.cal_store
|
||||
.delete_calendar(principal, cal_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{object_id}",
|
||||
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {
|
||||
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_report_calendar::<C, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
|
||||
{
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_mkcalendar::<C, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
128
crates/caldav/src/calendar/service.rs
Normal file
128
crates/caldav/src/calendar/service.rs
Normal file
@@ -0,0 +1,128 @@
|
||||
use crate::calendar::methods::mkcalendar::route_mkcalendar;
|
||||
use crate::calendar::methods::report::route_report_calendar;
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use crate::calendar_object::CalendarObjectResourceService;
|
||||
use crate::calendar_object::resource::CalendarObjectResource;
|
||||
use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use axum::extract::Request;
|
||||
use axum::handler::Handler;
|
||||
use axum::response::Response;
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use std::convert::Infallible;
|
||||
use std::sync::Arc;
|
||||
use tower::Service;
|
||||
|
||||
pub struct CalendarResourceService<C: CalendarStore, S: SubscriptionStore> {
|
||||
pub(crate) cal_store: Arc<C>,
|
||||
pub(crate) sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarResourceService<C, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
cal_store: self.cal_store.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> CalendarResourceService<C, S> {
|
||||
pub fn new(cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
cal_store,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourceService<C, S> {
|
||||
type MemberType = CalendarObjectResource;
|
||||
type PathComponents = (String, String); // principal, calendar_id
|
||||
type Resource = CalendarResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Error> {
|
||||
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
|
||||
Ok(CalendarResource {
|
||||
cal: calendar,
|
||||
read_only: self.cal_store.is_read_only(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
Ok(self
|
||||
.cal_store
|
||||
.get_objects(principal, cal_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|object| CalendarObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn save_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
file: Self::Resource,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.cal_store
|
||||
.update_calendar(principal.to_owned(), cal_id.to_owned(), file.into())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
(principal, cal_id): &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.cal_store
|
||||
.delete_calendar(principal, cal_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{object_id}",
|
||||
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {
|
||||
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_report_calendar::<C, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
|
||||
{
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_mkcalendar::<C, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
use super::resource::CalendarObjectPathComponents;
|
||||
use crate::Error;
|
||||
use crate::calendar_object::resource::CalendarObjectResourceService;
|
||||
use crate::calendar_object::{CalendarObjectPathComponents, CalendarObjectResourceService};
|
||||
use crate::error::Precondition;
|
||||
use axum::body::Body;
|
||||
use axum::extract::{Path, State};
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
pub mod methods;
|
||||
pub mod resource;
|
||||
mod service;
|
||||
pub use service::*;
|
||||
mod prop;
|
||||
pub use prop::*;
|
||||
|
||||
45
crates/caldav/src/calendar_object/prop.rs
Normal file
45
crates/caldav/src/calendar_object/prop.rs
Normal file
@@ -0,0 +1,45 @@
|
||||
use rustical_dav::extensions::CommonPropertiesProp;
|
||||
use rustical_ical::UtcDateTime;
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "CalendarObjectPropName")]
|
||||
pub enum CalendarObjectProp {
|
||||
// WebDAV (RFC 2518)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Getetag(String),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||
Getcontenttype(&'static str),
|
||||
|
||||
// CalDAV (RFC 4791)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
#[xml(prop = "CalendarData")]
|
||||
CalendarData(String),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
|
||||
pub enum CalendarObjectPropWrapper {
|
||||
CalendarObject(CalendarObjectProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct ExpandElement {
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) start: UtcDateTime,
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) end: UtcDateTime,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
|
||||
pub struct CalendarData {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) comp: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) expand: Option<ExpandElement>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) limit_recurrence_set: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) limit_freebusy_set: Option<()>,
|
||||
}
|
||||
@@ -1,66 +1,14 @@
|
||||
// use super::methods::{get_event, put_event};
|
||||
use crate::{
|
||||
CalDavPrincipalUri, Error,
|
||||
calendar_object::methods::{get_event, put_event},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use axum::{extract::Request, handler::Handler, response::Response};
|
||||
use super::prop::*;
|
||||
use crate::Error;
|
||||
use derive_more::derive::{From, Into};
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::{
|
||||
extensions::{CommonPropertiesExtension, CommonPropertiesProp},
|
||||
extensions::CommonPropertiesExtension,
|
||||
privileges::UserPrivilegeSet,
|
||||
resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService},
|
||||
resource::{PrincipalUri, Resource, ResourceName},
|
||||
xml::Resourcetype,
|
||||
};
|
||||
use rustical_ical::{CalendarObject, UtcDateTime};
|
||||
use rustical_store::{CalendarStore, auth::User};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use tower::Service;
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
pub(crate) struct ExpandElement {
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) start: UtcDateTime,
|
||||
#[xml(ty = "attr")]
|
||||
pub(crate) end: UtcDateTime,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
|
||||
pub struct CalendarData {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) comp: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) expand: Option<ExpandElement>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) limit_recurrence_set: Option<()>,
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
pub(crate) limit_freebusy_set: Option<()>,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "CalendarObjectPropName")]
|
||||
pub enum CalendarObjectProp {
|
||||
// WebDAV (RFC 2518)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Getetag(String),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||
Getcontenttype(&'static str),
|
||||
|
||||
// CalDAV (RFC 4791)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
#[xml(prop = "CalendarData")]
|
||||
CalendarData(String),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
|
||||
pub enum CalendarObjectPropWrapper {
|
||||
CalendarObject(CalendarObjectProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
use rustical_ical::CalendarObject;
|
||||
use rustical_store::auth::User;
|
||||
|
||||
#[derive(Clone, From, Into)]
|
||||
pub struct CalendarObjectResource {
|
||||
@@ -130,101 +78,3 @@ impl Resource for CalendarObjectResource {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let name: String = Deserialize::deserialize(deserializer)?;
|
||||
if let Some(object_id) = name.strip_suffix(".ics") {
|
||||
Ok(object_id.to_owned())
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Missing .ics extension"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct CalendarObjectPathComponents {
|
||||
pub principal: String,
|
||||
pub calendar_id: String,
|
||||
#[serde(deserialize_with = "deserialize_ics_name")]
|
||||
pub object_id: String,
|
||||
}
|
||||
|
||||
pub struct CalendarObjectResourceService<C: CalendarStore> {
|
||||
pub(crate) cal_store: Arc<C>,
|
||||
}
|
||||
|
||||
impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
cal_store: self.cal_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore> CalendarObjectResourceService<C> {
|
||||
pub fn new(cal_store: Arc<C>) -> Self {
|
||||
Self { cal_store }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
||||
type PathComponents = CalendarObjectPathComponents;
|
||||
type Resource = CalendarObjectResource;
|
||||
type MemberType = CalendarObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
CalendarObjectPathComponents {
|
||||
principal,
|
||||
calendar_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let object = self
|
||||
.cal_store
|
||||
.get_object(principal, calendar_id, object_id)
|
||||
.await?;
|
||||
Ok(CalendarObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
CalendarObjectPathComponents {
|
||||
principal,
|
||||
calendar_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.cal_store
|
||||
.delete_object(principal, calendar_id, object_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
|
||||
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(get_event::<C>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(put_event::<C>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
113
crates/caldav/src/calendar_object/service.rs
Normal file
113
crates/caldav/src/calendar_object/service.rs
Normal file
@@ -0,0 +1,113 @@
|
||||
use crate::{
|
||||
CalDavPrincipalUri, Error,
|
||||
calendar_object::{
|
||||
methods::{get_event, put_event},
|
||||
resource::CalendarObjectResource,
|
||||
},
|
||||
};
|
||||
use async_trait::async_trait;
|
||||
use axum::{extract::Request, handler::Handler, response::Response};
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::{CalendarStore, auth::User};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use tower::Service;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct CalendarObjectPathComponents {
|
||||
pub principal: String,
|
||||
pub calendar_id: String,
|
||||
#[serde(deserialize_with = "deserialize_ics_name")]
|
||||
pub object_id: String,
|
||||
}
|
||||
|
||||
pub struct CalendarObjectResourceService<C: CalendarStore> {
|
||||
pub(crate) cal_store: Arc<C>,
|
||||
}
|
||||
|
||||
impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
cal_store: self.cal_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore> CalendarObjectResourceService<C> {
|
||||
pub fn new(cal_store: Arc<C>) -> Self {
|
||||
Self { cal_store }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
||||
type PathComponents = CalendarObjectPathComponents;
|
||||
type Resource = CalendarObjectResource;
|
||||
type MemberType = CalendarObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
CalendarObjectPathComponents {
|
||||
principal,
|
||||
calendar_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let object = self
|
||||
.cal_store
|
||||
.get_object(principal, calendar_id, object_id)
|
||||
.await?;
|
||||
Ok(CalendarObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
CalendarObjectPathComponents {
|
||||
principal,
|
||||
calendar_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.cal_store
|
||||
.delete_object(principal, calendar_id, object_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
|
||||
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(get_event::<C>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(put_event::<C>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let name: String = Deserialize::deserialize(deserializer)?;
|
||||
if let Some(object_id) = name.strip_suffix(".ics") {
|
||||
Ok(object_id.to_owned())
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Missing .ics extension"))
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,14 @@
|
||||
use crate::calendar::resource::{CalendarResource, CalendarResourceService};
|
||||
use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
||||
use crate::Error;
|
||||
use rustical_dav::extensions::CommonPropertiesExtension;
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
||||
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
mod service;
|
||||
pub use service::*;
|
||||
mod prop;
|
||||
pub use prop::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct CalendarSetResource {
|
||||
@@ -24,12 +23,6 @@ impl ResourceName for CalendarSetResource {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||
pub enum PrincipalPropWrapper {
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
impl Resource for CalendarSetResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
@@ -67,77 +60,3 @@ impl Resource for CalendarSetResource {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CalendarSetResourceService<C: CalendarStore, S: SubscriptionStore> {
|
||||
name: &'static str,
|
||||
cal_store: Arc<C>,
|
||||
sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarSetResourceService<C, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name,
|
||||
cal_store: self.cal_store.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> CalendarSetResourceService<C, S> {
|
||||
pub fn new(name: &'static str, cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
cal_store,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetResourceService<C, S> {
|
||||
type PathComponents = (String,);
|
||||
type MemberType = CalendarResource;
|
||||
type Resource = CalendarSetResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
Ok(CalendarSetResource {
|
||||
principal: principal.to_owned(),
|
||||
read_only: self.cal_store.is_read_only(),
|
||||
name: self.name,
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
let calendars = self.cal_store.get_calendars(principal).await?;
|
||||
Ok(calendars
|
||||
.into_iter()
|
||||
.map(|cal| CalendarResource {
|
||||
cal,
|
||||
read_only: self.cal_store.is_read_only(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{calendar_id}",
|
||||
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
|
||||
.axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}
|
||||
|
||||
8
crates/caldav/src/calendar_set/prop.rs
Normal file
8
crates/caldav/src/calendar_set/prop.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
use rustical_dav::extensions::CommonPropertiesProp;
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||
pub enum PrincipalPropWrapper {
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
84
crates/caldav/src/calendar_set/service.rs
Normal file
84
crates/caldav/src/calendar_set/service.rs
Normal file
@@ -0,0 +1,84 @@
|
||||
use crate::calendar::CalendarResourceService;
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use crate::calendar_set::CalendarSetResource;
|
||||
use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct CalendarSetResourceService<C: CalendarStore, S: SubscriptionStore> {
|
||||
name: &'static str,
|
||||
cal_store: Arc<C>,
|
||||
sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarSetResourceService<C, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
name: self.name,
|
||||
cal_store: self.cal_store.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: CalendarStore, S: SubscriptionStore> CalendarSetResourceService<C, S> {
|
||||
pub fn new(name: &'static str, cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
name,
|
||||
cal_store,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetResourceService<C, S> {
|
||||
type PathComponents = (String,);
|
||||
type MemberType = CalendarResource;
|
||||
type Resource = CalendarSetResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
Ok(CalendarSetResource {
|
||||
principal: principal.to_owned(),
|
||||
read_only: self.cal_store.is_read_only(),
|
||||
name: self.name,
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
let calendars = self.cal_store.get_calendars(principal).await?;
|
||||
Ok(calendars
|
||||
.into_iter()
|
||||
.map(|cal| CalendarResource {
|
||||
cal,
|
||||
read_only: self.cal_store.is_read_only(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{calendar_id}",
|
||||
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
|
||||
.axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}
|
||||
@@ -1,16 +1,14 @@
|
||||
use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
|
||||
use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
||||
use crate::Error;
|
||||
use rustical_dav::extensions::CommonPropertiesExtension;
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
||||
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||
use rustical_store::auth::user::PrincipalType;
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use std::sync::Arc;
|
||||
use rustical_store::auth::User;
|
||||
|
||||
mod service;
|
||||
pub use service::*;
|
||||
mod prop;
|
||||
pub use prop::*;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct PrincipalResource {
|
||||
@@ -24,37 +22,6 @@ impl ResourceName for PrincipalResource {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||
pub enum PrincipalProp {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Displayname(String),
|
||||
|
||||
// Scheduling Extensions to CalDAV (RFC 6638)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)]
|
||||
CalendarUserType(PrincipalType),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
CalendarUserAddressSet(HrefElement),
|
||||
|
||||
// WebDAV Access Control (RFC 3744)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
|
||||
PrincipalUrl(HrefElement),
|
||||
|
||||
// CalDAV (RFC 4791)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
CalendarHomeSet(CalendarHomeSet),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||
pub enum PrincipalPropWrapper {
|
||||
Principal(PrincipalProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
impl Resource for PrincipalResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
@@ -124,104 +91,3 @@ impl Resource for PrincipalResource {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PrincipalResourceService<
|
||||
AP: AuthenticationProvider,
|
||||
S: SubscriptionStore,
|
||||
CS: CalendarStore,
|
||||
BS: CalendarStore,
|
||||
> {
|
||||
pub(crate) auth_provider: Arc<AP>,
|
||||
pub(crate) sub_store: Arc<S>,
|
||||
pub(crate) cal_store: Arc<CS>,
|
||||
pub(crate) birthday_store: Arc<BS>,
|
||||
}
|
||||
|
||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> Clone
|
||||
for PrincipalResourceService<AP, S, CS, BS>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
auth_provider: self.auth_provider.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
cal_store: self.cal_store.clone(),
|
||||
birthday_store: self.birthday_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
||||
ResourceService for PrincipalResourceService<AP, S, CS, BS>
|
||||
{
|
||||
type PathComponents = (String,);
|
||||
type MemberType = CalendarSetResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let user = self
|
||||
.auth_provider
|
||||
.get_principal(principal)
|
||||
.await?
|
||||
.ok_or(crate::Error::NotFound)?;
|
||||
Ok(PrincipalResource {
|
||||
principal: user,
|
||||
home_set: &["calendar", "birthdays"],
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
Ok(vec![
|
||||
CalendarSetResource {
|
||||
name: "calendar",
|
||||
principal: principal.to_owned(),
|
||||
read_only: false,
|
||||
},
|
||||
CalendarSetResource {
|
||||
name: "birthdays",
|
||||
principal: principal.to_owned(),
|
||||
read_only: true,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/calendar",
|
||||
CalendarSetResourceService::new(
|
||||
"calendar",
|
||||
self.cal_store.clone(),
|
||||
self.sub_store.clone(),
|
||||
)
|
||||
.axum_router(),
|
||||
)
|
||||
.nest(
|
||||
"/birthdays",
|
||||
CalendarSetResourceService::new(
|
||||
"birthdays",
|
||||
self.birthday_store.clone(),
|
||||
self.sub_store.clone(),
|
||||
)
|
||||
.axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
||||
AxumMethods for PrincipalResourceService<AP, S, CS, BS>
|
||||
{
|
||||
}
|
||||
|
||||
34
crates/caldav/src/principal/prop.rs
Normal file
34
crates/caldav/src/principal/prop.rs
Normal file
@@ -0,0 +1,34 @@
|
||||
use rustical_dav::{extensions::CommonPropertiesProp, xml::HrefElement};
|
||||
use rustical_store::auth::user::PrincipalType;
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||
pub enum PrincipalProp {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Displayname(String),
|
||||
|
||||
// Scheduling Extensions to CalDAV (RFC 6638)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)]
|
||||
CalendarUserType(PrincipalType),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
CalendarUserAddressSet(HrefElement),
|
||||
|
||||
// WebDAV Access Control (RFC 3744)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
|
||||
PrincipalUrl(HrefElement),
|
||||
|
||||
// CalDAV (RFC 4791)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
|
||||
CalendarHomeSet(CalendarHomeSet),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||
pub enum PrincipalPropWrapper {
|
||||
Principal(PrincipalProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] pub(super) Vec<HrefElement>);
|
||||
110
crates/caldav/src/principal/service.rs
Normal file
110
crates/caldav/src/principal/service.rs
Normal file
@@ -0,0 +1,110 @@
|
||||
use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
|
||||
use crate::principal::PrincipalResource;
|
||||
use crate::{CalDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::{CalendarStore, SubscriptionStore};
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PrincipalResourceService<
|
||||
AP: AuthenticationProvider,
|
||||
S: SubscriptionStore,
|
||||
CS: CalendarStore,
|
||||
BS: CalendarStore,
|
||||
> {
|
||||
pub(crate) auth_provider: Arc<AP>,
|
||||
pub(crate) sub_store: Arc<S>,
|
||||
pub(crate) cal_store: Arc<CS>,
|
||||
pub(crate) birthday_store: Arc<BS>,
|
||||
}
|
||||
|
||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> Clone
|
||||
for PrincipalResourceService<AP, S, CS, BS>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
auth_provider: self.auth_provider.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
cal_store: self.cal_store.clone(),
|
||||
birthday_store: self.birthday_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
||||
ResourceService for PrincipalResourceService<AP, S, CS, BS>
|
||||
{
|
||||
type PathComponents = (String,);
|
||||
type MemberType = CalendarSetResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let user = self
|
||||
.auth_provider
|
||||
.get_principal(principal)
|
||||
.await?
|
||||
.ok_or(crate::Error::NotFound)?;
|
||||
Ok(PrincipalResource {
|
||||
principal: user,
|
||||
home_set: &["calendar", "birthdays"],
|
||||
})
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
Ok(vec![
|
||||
CalendarSetResource {
|
||||
name: "calendar",
|
||||
principal: principal.to_owned(),
|
||||
read_only: false,
|
||||
},
|
||||
CalendarSetResource {
|
||||
name: "birthdays",
|
||||
principal: principal.to_owned(),
|
||||
read_only: true,
|
||||
},
|
||||
])
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/calendar",
|
||||
CalendarSetResourceService::new(
|
||||
"calendar",
|
||||
self.cal_store.clone(),
|
||||
self.sub_store.clone(),
|
||||
)
|
||||
.axum_router(),
|
||||
)
|
||||
.nest(
|
||||
"/birthdays",
|
||||
CalendarSetResourceService::new(
|
||||
"birthdays",
|
||||
self.birthday_store.clone(),
|
||||
self.sub_store.clone(),
|
||||
)
|
||||
.axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
|
||||
AxumMethods for PrincipalResourceService<AP, S, CS, BS>
|
||||
{
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::resource::AddressObjectPathComponents;
|
||||
use super::AddressObjectPathComponents;
|
||||
use super::AddressObjectResourceService;
|
||||
use crate::Error;
|
||||
use crate::address_object::resource::AddressObjectResourceService;
|
||||
use crate::addressbook::resource::AddressbookResource;
|
||||
use axum::body::Body;
|
||||
use axum::extract::{Path, State};
|
||||
@@ -15,6 +13,7 @@ use rustical_dav::resource::Resource;
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::AddressbookStore;
|
||||
use rustical_store::auth::User;
|
||||
use std::str::FromStr;
|
||||
use tracing::instrument;
|
||||
|
||||
#[instrument(skip(addr_store))]
|
||||
|
||||
@@ -1,2 +1,6 @@
|
||||
pub mod methods;
|
||||
pub mod resource;
|
||||
mod service;
|
||||
pub use service::*;
|
||||
mod prop;
|
||||
pub use prop::*;
|
||||
|
||||
23
crates/carddav/src/address_object/prop.rs
Normal file
23
crates/carddav/src/address_object/prop.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use rustical_dav::extensions::CommonPropertiesProp;
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressObjectPropName")]
|
||||
pub enum AddressObjectProp {
|
||||
// WebDAV (RFC 2518)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Getetag(String),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||
Getcontenttype(&'static str),
|
||||
|
||||
// CalDAV (RFC 4791)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressData(String),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
|
||||
pub enum AddressObjectPropWrapper {
|
||||
AddressObject(AddressObjectProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
@@ -1,56 +1,19 @@
|
||||
use crate::{CardDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::{extract::Request, handler::Handler, response::Response};
|
||||
use derive_more::derive::{Constructor, From, Into};
|
||||
use futures_util::future::BoxFuture;
|
||||
use crate::{
|
||||
Error,
|
||||
address_object::{
|
||||
AddressObjectProp, AddressObjectPropName, AddressObjectPropWrapper,
|
||||
AddressObjectPropWrapperName,
|
||||
},
|
||||
};
|
||||
use derive_more::derive::{From, Into};
|
||||
use rustical_dav::{
|
||||
extensions::{CommonPropertiesExtension, CommonPropertiesProp},
|
||||
extensions::CommonPropertiesExtension,
|
||||
privileges::UserPrivilegeSet,
|
||||
resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService},
|
||||
resource::{PrincipalUri, Resource, ResourceName},
|
||||
xml::Resourcetype,
|
||||
};
|
||||
use rustical_ical::AddressObject;
|
||||
use rustical_store::{AddressbookStore, auth::User};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use tower::Service;
|
||||
|
||||
use super::methods::{get_object, put_object};
|
||||
|
||||
#[derive(Constructor)]
|
||||
pub struct AddressObjectResourceService<AS: AddressbookStore> {
|
||||
pub(crate) addr_store: Arc<AS>,
|
||||
}
|
||||
|
||||
impl<AS: AddressbookStore> Clone for AddressObjectResourceService<AS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
addr_store: self.addr_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressObjectPropName")]
|
||||
pub enum AddressObjectProp {
|
||||
// WebDAV (RFC 2518)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Getetag(String),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
|
||||
Getcontenttype(&'static str),
|
||||
|
||||
// CalDAV (RFC 4791)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressData(String),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
|
||||
pub enum AddressObjectPropWrapper {
|
||||
AddressObject(AddressObjectProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
use rustical_store::auth::User;
|
||||
|
||||
#[derive(Clone, From, Into)]
|
||||
pub struct AddressObjectResource {
|
||||
@@ -113,84 +76,3 @@ impl Resource for AddressObjectResource {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_vcf_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let name: String = Deserialize::deserialize(deserializer)?;
|
||||
if let Some(object_id) = name.strip_suffix(".vcf") {
|
||||
Ok(object_id.to_owned())
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Missing .vcf extension"))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct AddressObjectPathComponents {
|
||||
pub principal: String,
|
||||
pub addressbook_id: String,
|
||||
#[serde(deserialize_with = "deserialize_vcf_name")]
|
||||
pub object_id: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS> {
|
||||
type PathComponents = AddressObjectPathComponents;
|
||||
type Resource = AddressObjectResource;
|
||||
type MemberType = AddressObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
AddressObjectPathComponents {
|
||||
principal,
|
||||
addressbook_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let object = self
|
||||
.addr_store
|
||||
.get_object(principal, addressbook_id, object_id, false)
|
||||
.await?;
|
||||
Ok(AddressObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
AddressObjectPathComponents {
|
||||
principal,
|
||||
addressbook_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.addr_store
|
||||
.delete_object(principal, addressbook_id, object_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS: AddressbookStore> AxumMethods for AddressObjectResourceService<AS> {
|
||||
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(get_object::<AS>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(put_object::<AS>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
105
crates/carddav/src/address_object/service.rs
Normal file
105
crates/carddav/src/address_object/service.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use super::methods::{get_object, put_object};
|
||||
use crate::{CardDavPrincipalUri, Error, address_object::resource::AddressObjectResource};
|
||||
use async_trait::async_trait;
|
||||
use axum::{extract::Request, handler::Handler, response::Response};
|
||||
use derive_more::derive::Constructor;
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::{AddressbookStore, auth::User};
|
||||
use serde::{Deserialize, Deserializer};
|
||||
use std::{convert::Infallible, sync::Arc};
|
||||
use tower::Service;
|
||||
|
||||
#[derive(Constructor)]
|
||||
pub struct AddressObjectResourceService<AS: AddressbookStore> {
|
||||
pub(crate) addr_store: Arc<AS>,
|
||||
}
|
||||
|
||||
impl<AS: AddressbookStore> Clone for AddressObjectResourceService<AS> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
addr_store: self.addr_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct AddressObjectPathComponents {
|
||||
pub principal: String,
|
||||
pub addressbook_id: String,
|
||||
#[serde(deserialize_with = "deserialize_vcf_name")]
|
||||
pub object_id: String,
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS> {
|
||||
type PathComponents = AddressObjectPathComponents;
|
||||
type Resource = AddressObjectResource;
|
||||
type MemberType = AddressObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
AddressObjectPathComponents {
|
||||
principal,
|
||||
addressbook_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let object = self
|
||||
.addr_store
|
||||
.get_object(principal, addressbook_id, object_id, false)
|
||||
.await?;
|
||||
Ok(AddressObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
AddressObjectPathComponents {
|
||||
principal,
|
||||
addressbook_id,
|
||||
object_id,
|
||||
}: &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.addr_store
|
||||
.delete_object(principal, addressbook_id, object_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS: AddressbookStore> AxumMethods for AddressObjectResourceService<AS> {
|
||||
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(get_object::<AS>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(put_object::<AS>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_vcf_name<'de, D>(deserializer: D) -> Result<String, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let name: String = Deserialize::deserialize(deserializer)?;
|
||||
if let Some(object_id) = name.strip_suffix(".vcf") {
|
||||
Ok(object_id.to_owned())
|
||||
} else {
|
||||
Err(serde::de::Error::custom("Missing .vcf extension"))
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{Error, addressbook::resource::AddressbookResourceService};
|
||||
use crate::{Error, addressbook::AddressbookResourceService};
|
||||
use axum::{
|
||||
extract::{Path, State},
|
||||
response::{IntoResponse, Response},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
Error,
|
||||
address_object::resource::{
|
||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
|
||||
address_object::{
|
||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
|
||||
},
|
||||
};
|
||||
use http::StatusCode;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
CardDavPrincipalUri, Error, address_object::resource::AddressObjectPropWrapperName,
|
||||
addressbook::resource::AddressbookResourceService,
|
||||
CardDavPrincipalUri, Error, address_object::AddressObjectPropWrapperName,
|
||||
addressbook::AddressbookResourceService,
|
||||
};
|
||||
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
|
||||
use axum::{
|
||||
@@ -81,7 +81,7 @@ pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::address_object::resource::AddressObjectPropName;
|
||||
use crate::address_object::AddressObjectPropName;
|
||||
use rustical_dav::xml::{PropElement, sync_collection::SyncLevel};
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
Error,
|
||||
address_object::resource::{
|
||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
|
||||
address_object::{
|
||||
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
|
||||
},
|
||||
};
|
||||
use http::StatusCode;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
pub mod methods;
|
||||
pub mod prop;
|
||||
pub mod resource;
|
||||
mod service;
|
||||
pub use service::*;
|
||||
|
||||
@@ -1,4 +1,33 @@
|
||||
use rustical_xml::XmlSerialize;
|
||||
use rustical_dav::extensions::{CommonPropertiesProp, SyncTokenExtensionProp};
|
||||
use rustical_dav_push::DavPushExtensionProp;
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressbookPropName")]
|
||||
pub enum AddressbookProp {
|
||||
// WebDAV (RFC 2518)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Displayname(Option<String>),
|
||||
|
||||
// CardDAV (RFC 6352)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookDescription(Option<String>),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
||||
SupportedAddressData(SupportedAddressData),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
||||
SupportedReportSet(SupportedReportSet),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
MaxResourceSize(i64),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
|
||||
pub enum AddressbookPropWrapper {
|
||||
Addressbook(AddressbookProp),
|
||||
SyncToken(SyncTokenExtensionProp),
|
||||
DavPush(DavPushExtensionProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||
pub struct AddressDataType {
|
||||
|
||||
@@ -1,78 +1,16 @@
|
||||
use super::methods::mkcol::route_mkcol;
|
||||
use super::methods::report::route_report_addressbook;
|
||||
use super::prop::{SupportedAddressData, SupportedReportSet};
|
||||
use crate::address_object::resource::{AddressObjectResource, AddressObjectResourceService};
|
||||
use crate::{CardDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use axum::extract::Request;
|
||||
use axum::handler::Handler;
|
||||
use axum::response::Response;
|
||||
use derive_more::derive::{From, Into};
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::extensions::{
|
||||
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
||||
use crate::Error;
|
||||
use crate::addressbook::prop::{
|
||||
AddressbookProp, AddressbookPropName, AddressbookPropWrapper, AddressbookPropWrapperName,
|
||||
};
|
||||
use derive_more::derive::{From, Into};
|
||||
use rustical_dav::extensions::{CommonPropertiesExtension, SyncTokenExtension};
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
||||
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
||||
use rustical_dav_push::DavPushExtension;
|
||||
use rustical_store::Addressbook;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use std::convert::Infallible;
|
||||
use std::sync::Arc;
|
||||
use tower::Service;
|
||||
|
||||
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
|
||||
pub(crate) addr_store: Arc<AS>,
|
||||
pub(crate) sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S> {
|
||||
pub fn new(addr_store: Arc<A>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
addr_store,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, S: SubscriptionStore> Clone for AddressbookResourceService<A, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
addr_store: self.addr_store.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressbookPropName")]
|
||||
pub enum AddressbookProp {
|
||||
// WebDAV (RFC 2518)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Displayname(Option<String>),
|
||||
|
||||
// CardDAV (RFC 6352)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookDescription(Option<String>),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
||||
SupportedAddressData(SupportedAddressData),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
|
||||
SupportedReportSet(SupportedReportSet),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
MaxResourceSize(i64),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
|
||||
pub enum AddressbookPropWrapper {
|
||||
Addressbook(AddressbookProp),
|
||||
SyncToken(SyncTokenExtensionProp),
|
||||
DavPush(DavPushExtensionProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, From, Into)]
|
||||
pub struct AddressbookResource(pub(crate) Addressbook);
|
||||
@@ -205,92 +143,3 @@ impl Resource for AddressbookResource {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
||||
for AddressbookResourceService<AS, S>
|
||||
{
|
||||
type MemberType = AddressObjectResource;
|
||||
type PathComponents = (String, String); // principal, addressbook_id
|
||||
type Resource = AddressbookResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Error> {
|
||||
let addressbook = self
|
||||
.addr_store
|
||||
.get_addressbook(principal, addressbook_id, false)
|
||||
.await
|
||||
.map_err(|_e| Error::NotFound)?;
|
||||
Ok(addressbook.into())
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
Ok(self
|
||||
.addr_store
|
||||
.get_objects(principal, addressbook_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|object| AddressObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn save_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
file: Self::Resource,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.addr_store
|
||||
.update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.addr_store
|
||||
.delete_addressbook(principal, addressbook_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{object_id}",
|
||||
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {
|
||||
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_report_addressbook::<AS, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn mkcol() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_mkcol::<AS, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
130
crates/carddav/src/addressbook/service.rs
Normal file
130
crates/carddav/src/addressbook/service.rs
Normal file
@@ -0,0 +1,130 @@
|
||||
use super::methods::mkcol::route_mkcol;
|
||||
use super::methods::report::route_report_addressbook;
|
||||
use crate::address_object::AddressObjectResourceService;
|
||||
use crate::address_object::resource::AddressObjectResource;
|
||||
use crate::addressbook::resource::AddressbookResource;
|
||||
use crate::{CardDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use axum::extract::Request;
|
||||
use axum::handler::Handler;
|
||||
use axum::response::Response;
|
||||
use futures_util::future::BoxFuture;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||
use std::convert::Infallible;
|
||||
use std::sync::Arc;
|
||||
use tower::Service;
|
||||
|
||||
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
|
||||
pub(crate) addr_store: Arc<AS>,
|
||||
pub(crate) sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S> {
|
||||
pub fn new(addr_store: Arc<A>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
addr_store,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, S: SubscriptionStore> Clone for AddressbookResourceService<A, S> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
addr_store: self.addr_store.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
||||
for AddressbookResourceService<AS, S>
|
||||
{
|
||||
type MemberType = AddressObjectResource;
|
||||
type PathComponents = (String, String); // principal, addressbook_id
|
||||
type Resource = AddressbookResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Error> {
|
||||
let addressbook = self
|
||||
.addr_store
|
||||
.get_addressbook(principal, addressbook_id, false)
|
||||
.await
|
||||
.map_err(|_e| Error::NotFound)?;
|
||||
Ok(addressbook.into())
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
Ok(self
|
||||
.addr_store
|
||||
.get_objects(principal, addressbook_id)
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(|object| AddressObjectResource {
|
||||
object,
|
||||
principal: principal.to_owned(),
|
||||
})
|
||||
.collect())
|
||||
}
|
||||
|
||||
async fn save_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
file: Self::Resource,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.addr_store
|
||||
.update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into())
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn delete_resource(
|
||||
&self,
|
||||
(principal, addressbook_id): &Self::PathComponents,
|
||||
use_trashbin: bool,
|
||||
) -> Result<(), Self::Error> {
|
||||
self.addr_store
|
||||
.delete_addressbook(principal, addressbook_id, use_trashbin)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{object_id}",
|
||||
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {
|
||||
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_report_addressbook::<AS, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
|
||||
fn mkcol() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
|
||||
Some(|state, req| {
|
||||
let mut service = Handler::with_state(route_mkcol::<AS, S>, state);
|
||||
Box::pin(Service::call(&mut service, req))
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,49 +1,14 @@
|
||||
use crate::addressbook::resource::{AddressbookResource, AddressbookResourceService};
|
||||
use crate::{CardDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
||||
use crate::Error;
|
||||
use rustical_dav::extensions::CommonPropertiesExtension;
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
|
||||
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
|
||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
use std::sync::Arc;
|
||||
use rustical_store::auth::User;
|
||||
|
||||
pub struct PrincipalResourceService<
|
||||
A: AddressbookStore,
|
||||
AP: AuthenticationProvider,
|
||||
S: SubscriptionStore,
|
||||
> {
|
||||
addr_store: Arc<A>,
|
||||
auth_provider: Arc<AP>,
|
||||
sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Clone
|
||||
for PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
addr_store: self.addr_store.clone(),
|
||||
auth_provider: self.auth_provider.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore>
|
||||
PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
pub fn new(addr_store: Arc<A>, auth_provider: Arc<AP>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
addr_store,
|
||||
auth_provider,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
mod service;
|
||||
pub use service::*;
|
||||
mod prop;
|
||||
pub use prop::*;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PrincipalResource {
|
||||
@@ -56,34 +21,6 @@ impl ResourceName for PrincipalResource {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||
pub enum PrincipalProp {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Displayname(String),
|
||||
|
||||
// WebDAV Access Control (RFC 3744)
|
||||
#[xml(rename = b"principal-URL")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
PrincipalUrl(HrefElement),
|
||||
|
||||
// CardDAV (RFC 6352)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookHomeSet(AddressbookHomeSet),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
PrincipalAddress(Option<HrefElement>),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||
pub enum PrincipalPropWrapper {
|
||||
Principal(PrincipalProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
|
||||
impl Resource for PrincipalResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
@@ -145,55 +82,3 @@ impl Resource for PrincipalResource {
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> ResourceService
|
||||
for PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
type PathComponents = (String,);
|
||||
type MemberType = AddressbookResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let user = self
|
||||
.auth_provider
|
||||
.get_principal(principal)
|
||||
.await?
|
||||
.ok_or(crate::Error::NotFound)?;
|
||||
Ok(PrincipalResource { principal: user })
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
let addressbooks = self.addr_store.get_addressbooks(principal).await?;
|
||||
Ok(addressbooks
|
||||
.into_iter()
|
||||
.map(AddressbookResource::from)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{addressbook_id}",
|
||||
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
|
||||
.axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods
|
||||
for PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
}
|
||||
|
||||
30
crates/carddav/src/principal/prop.rs
Normal file
30
crates/carddav/src/principal/prop.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use rustical_dav::{extensions::CommonPropertiesProp, xml::HrefElement};
|
||||
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] pub(super) Vec<HrefElement>);
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropName")]
|
||||
pub enum PrincipalProp {
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
Displayname(String),
|
||||
|
||||
// WebDAV Access Control (RFC 3744)
|
||||
#[xml(rename = b"principal-URL")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
PrincipalUrl(HrefElement),
|
||||
|
||||
// CardDAV (RFC 6352)
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
AddressbookHomeSet(AddressbookHomeSet),
|
||||
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
|
||||
PrincipalAddress(Option<HrefElement>),
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
|
||||
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
|
||||
pub enum PrincipalPropWrapper {
|
||||
Principal(PrincipalProp),
|
||||
Common(CommonPropertiesProp),
|
||||
}
|
||||
96
crates/carddav/src/principal/service.rs
Normal file
96
crates/carddav/src/principal/service.rs
Normal file
@@ -0,0 +1,96 @@
|
||||
use crate::addressbook::AddressbookResourceService;
|
||||
use crate::addressbook::resource::AddressbookResource;
|
||||
use crate::principal::PrincipalResource;
|
||||
use crate::{CardDavPrincipalUri, Error};
|
||||
use async_trait::async_trait;
|
||||
use axum::Router;
|
||||
use rustical_dav::resource::{AxumMethods, ResourceService};
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::{AddressbookStore, SubscriptionStore};
|
||||
use std::sync::Arc;
|
||||
|
||||
pub struct PrincipalResourceService<
|
||||
A: AddressbookStore,
|
||||
AP: AuthenticationProvider,
|
||||
S: SubscriptionStore,
|
||||
> {
|
||||
addr_store: Arc<A>,
|
||||
auth_provider: Arc<AP>,
|
||||
sub_store: Arc<S>,
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Clone
|
||||
for PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
addr_store: self.addr_store.clone(),
|
||||
auth_provider: self.auth_provider.clone(),
|
||||
sub_store: self.sub_store.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore>
|
||||
PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
pub fn new(addr_store: Arc<A>, auth_provider: Arc<AP>, sub_store: Arc<S>) -> Self {
|
||||
Self {
|
||||
addr_store,
|
||||
auth_provider,
|
||||
sub_store,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> ResourceService
|
||||
for PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
type PathComponents = (String,);
|
||||
type MemberType = AddressbookResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
type PrincipalUri = CardDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Self::Resource, Self::Error> {
|
||||
let user = self
|
||||
.auth_provider
|
||||
.get_principal(principal)
|
||||
.await?
|
||||
.ok_or(crate::Error::NotFound)?;
|
||||
Ok(PrincipalResource { principal: user })
|
||||
}
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
(principal,): &Self::PathComponents,
|
||||
) -> Result<Vec<Self::MemberType>, Self::Error> {
|
||||
let addressbooks = self.addr_store.get_addressbooks(principal).await?;
|
||||
Ok(addressbooks
|
||||
.into_iter()
|
||||
.map(AddressbookResource::from)
|
||||
.collect())
|
||||
}
|
||||
|
||||
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
|
||||
Router::new()
|
||||
.nest(
|
||||
"/{addressbook_id}",
|
||||
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
|
||||
.axum_router(),
|
||||
)
|
||||
.route_service("/", self.axum_service())
|
||||
}
|
||||
}
|
||||
|
||||
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods
|
||||
for PrincipalResourceService<A, AP, S>
|
||||
{
|
||||
}
|
||||
Reference in New Issue
Block a user