use crate::addressbook::resource::{AddressbookResource, AddressbookResourceService}; use crate::{CardDavPrincipalUri, Error}; use actix_web::web; use async_trait::async_trait; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::resource::{PrincipalUri, Resource, ResourceService}; 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; pub struct PrincipalResourceService< A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore, > { addr_store: Arc, auth_provider: Arc, sub_store: Arc, } impl Clone for PrincipalResourceService { fn clone(&self) -> Self { Self { addr_store: self.addr_store.clone(), auth_provider: self.auth_provider.clone(), sub_store: self.sub_store.clone(), } } } impl PrincipalResourceService { pub fn new(addr_store: Arc, auth_provider: Arc, sub_store: Arc) -> Self { Self { addr_store, auth_provider, sub_store, } } } #[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, 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), } #[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; 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, puri: &impl PrincipalUri, user: &User, prop: &PrincipalPropWrapperName, ) -> Result { let principal_href = HrefElement::new(puri.principal_uri(&user.id)); let home_set = AddressbookHomeSet( user.memberships() .into_iter() .map(|principal| puri.principal_uri(principal)) .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, puri, 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] impl ResourceService for PrincipalResourceService { 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 { 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()) } fn actix_scope(self) -> actix_web::Scope { web::scope("/principal/{principal}") .service( AddressbookResourceService::<_, S>::new( self.addr_store.clone(), self.sub_store.clone(), ) .actix_scope(), ) .service(self.actix_resource()) } }