use crate::Error; use crate::addressbook::resource::AddressbookResource; use actix_web::dev::ResourceMap; use async_trait::async_trait; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{NamedRoute, Resource, ResourceService}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_store::AddressbookStore; use rustical_store::auth::{AuthenticationProvider, User}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use std::sync::Arc; pub struct PrincipalResourceService { addr_store: Arc, auth_provider: Arc, } impl PrincipalResourceService { pub fn new(addr_store: Arc, auth_provider: Arc) -> Self { Self { addr_store, auth_provider, } } } #[derive(Debug, Clone)] pub struct PrincipalResource { principal: User, } #[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)] pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] Vec); #[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] #[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), } #[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, EnumUnitVariants)] #[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)] pub enum PrincipalPropWrapper { Principal(PrincipalProp), Common(CommonPropertiesProp), } impl PrincipalResource { pub fn get_principal_url(rmap: &ResourceMap, principal: &str) -> String { Self::get_url(rmap, vec![principal]).unwrap() } } impl NamedRoute for PrincipalResource { fn route_name() -> &'static str { "carddav_principal" } } impl Resource for PrincipalResource { type Prop = PrincipalPropWrapper; type Error = Error; type PrincipalResource = PrincipalResource; type Principal = User; fn get_resourcetype(&self) -> Resourcetype { Resourcetype(&[ ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"), ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "principal"), ]) } fn get_prop( &self, rmap: &ResourceMap, user: &User, prop: &PrincipalPropWrapperName, ) -> Result { let principal_href = HrefElement::new(Self::get_principal_url(rmap, &self.principal.id)); let home_set = AddressbookHomeSet( user.memberships() .into_iter() .map(|principal| Self::get_url(rmap, vec![principal]).unwrap()) .map(HrefElement::new) .collect(), ); Ok(match prop { PrincipalPropWrapperName::Principal(prop) => { PrincipalPropWrapper::Principal(match prop { PrincipalPropName::Displayname => PrincipalProp::Displayname( self.principal .displayname .to_owned() .unwrap_or(self.principal.id.to_owned()), ), PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href), PrincipalPropName::AddressbookHomeSet => { PrincipalProp::AddressbookHomeSet(home_set) } PrincipalPropName::PrincipalAddress => PrincipalProp::PrincipalAddress(None), }) } PrincipalPropWrapperName::Common(prop) => PrincipalPropWrapper::Common( CommonPropertiesExtension::get_prop(self, rmap, user, prop)?, ), }) } fn get_owner(&self) -> Option<&str> { Some(&self.principal.id) } fn get_user_privileges(&self, user: &User) -> Result { Ok(UserPrivilegeSet::owner_only( user.is_principal(&self.principal.id), )) } } #[async_trait(?Send)] impl ResourceService for PrincipalResourceService { type PathComponents = (String,); type MemberType = AddressbookResource; type Resource = PrincipalResource; type Error = Error; type Principal = User; async fn get_resource( &self, (principal,): &Self::PathComponents, ) -> Result { 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, Self::Error> { let addressbooks = self.addr_store.get_addressbooks(principal).await?; Ok(addressbooks .into_iter() .map(|addressbook| (addressbook.id.to_owned(), addressbook.into())) .collect()) } }