dav: Make reusable for other projects

This commit is contained in:
Lennart
2025-04-18 13:26:44 +02:00
parent 626eff0373
commit 54e327d764
36 changed files with 210 additions and 121 deletions

25
Cargo.lock generated
View File

@@ -3035,6 +3035,7 @@ dependencies = [
"rustical_caldav", "rustical_caldav",
"rustical_carddav", "rustical_carddav",
"rustical_dav", "rustical_dav",
"rustical_dav_push",
"rustical_frontend", "rustical_frontend",
"rustical_store", "rustical_store",
"rustical_store_sqlite", "rustical_store_sqlite",
@@ -3063,6 +3064,7 @@ dependencies = [
"futures-util", "futures-util",
"quick-xml", "quick-xml",
"rustical_dav", "rustical_dav",
"rustical_dav_push",
"rustical_store", "rustical_store",
"rustical_xml", "rustical_xml",
"serde", "serde",
@@ -3088,6 +3090,7 @@ dependencies = [
"futures-util", "futures-util",
"quick-xml", "quick-xml",
"rustical_dav", "rustical_dav",
"rustical_dav_push",
"rustical_store", "rustical_store",
"rustical_xml", "rustical_xml",
"serde", "serde",
@@ -3111,6 +3114,27 @@ dependencies = [
"log", "log",
"quick-xml", "quick-xml",
"reqwest", "reqwest",
"rustical_xml",
"serde",
"thiserror 2.0.12",
"tokio",
"tracing",
"tracing-actix-web",
]
[[package]]
name = "rustical_dav_push"
version = "0.1.0"
dependencies = [
"actix-web",
"async-trait",
"derive_more 2.0.1",
"futures-util",
"itertools 0.14.0",
"log",
"quick-xml",
"reqwest",
"rustical_dav",
"rustical_store", "rustical_store",
"rustical_xml", "rustical_xml",
"serde", "serde",
@@ -3167,6 +3191,7 @@ dependencies = [
"regex", "regex",
"rstest", "rstest",
"rstest_reuse", "rstest_reuse",
"rustical_dav",
"rustical_store_sqlite", "rustical_store_sqlite",
"rustical_xml", "rustical_xml",
"serde", "serde",

View File

@@ -88,6 +88,7 @@ sqlx-sqlite = { version = "0.8", features = ["bundled"] }
ical = { version = "0.11", features = ["generator", "serde"] } ical = { version = "0.11", features = ["generator", "serde"] }
toml = "0.8" toml = "0.8"
rustical_dav = { path = "./crates/dav/" } rustical_dav = { path = "./crates/dav/" }
rustical_dav_push = { path = "./crates/dav_push/" }
rustical_store = { path = "./crates/store/" } rustical_store = { path = "./crates/store/" }
rustical_store_sqlite = { path = "./crates/store_sqlite/" } rustical_store_sqlite = { path = "./crates/store_sqlite/" }
rustical_caldav = { path = "./crates/caldav/" } rustical_caldav = { path = "./crates/caldav/" }
@@ -155,4 +156,5 @@ pbkdf2.workspace = true
password-hash.workspace = true password-hash.workspace = true
reqwest.workspace = true reqwest.workspace = true
rustical_dav.workspace = true rustical_dav.workspace = true
rustical_dav_push.workspace = true
quick-xml.workspace = true quick-xml.workspace = true

View File

@@ -27,3 +27,4 @@ chrono-tz = { workspace = true }
sha2 = { workspace = true } sha2 = { workspace = true }
rustical_xml.workspace = true rustical_xml.workspace = true
uuid.workspace = true uuid.workspace = true
rustical_dav_push.workspace = true

View File

@@ -1,11 +1,11 @@
use crate::calendar::resource::CalendarResource;
use crate::Error; use crate::Error;
use crate::calendar::resource::CalendarResource;
use actix_web::http::header; use actix_web::http::header;
use actix_web::web::{Data, Path}; use actix_web::web::{Data, Path};
use actix_web::{HttpRequest, HttpResponse}; use actix_web::{HttpRequest, HttpResponse};
use rustical_dav::privileges::UserPrivilege; use rustical_dav::privileges::UserPrivilege;
use rustical_dav::push::PushRegister;
use rustical_dav::resource::Resource; use rustical_dav::resource::Resource;
use rustical_dav_push::register::PushRegister;
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::{CalendarStore, Subscription, SubscriptionStore}; use rustical_store::{CalendarStore, Subscription, SubscriptionStore};
use rustical_xml::XmlDocument; use rustical_xml::XmlDocument;

View File

@@ -2,9 +2,9 @@ use super::methods::mkcalendar::route_mkcalendar;
use super::methods::post::route_post; use super::methods::post::route_post;
use super::methods::report::route_report_calendar; use super::methods::report::route_report_calendar;
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet}; use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
use crate::Error;
use crate::calendar_object::resource::CalendarObjectResource; use crate::calendar_object::resource::CalendarObjectResource;
use crate::principal::PrincipalResource; use crate::principal::PrincipalResource;
use crate::Error;
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use actix_web::http::Method; use actix_web::http::Method;
use actix_web::web; use actix_web::web;
@@ -12,12 +12,12 @@ use async_trait::async_trait;
use chrono::{DateTime, Utc}; use chrono::{DateTime, Utc};
use derive_more::derive::{From, Into}; use derive_more::derive::{From, Into};
use rustical_dav::extensions::{ use rustical_dav::extensions::{
CommonPropertiesExtension, CommonPropertiesProp, DavPushExtension, DavPushExtensionProp, CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
SyncTokenExtension, SyncTokenExtensionProp,
}; };
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{Resource, ResourceService}; use rustical_dav::resource::{Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::calendar::CalDateTime; use rustical_store::calendar::CalDateTime;
use rustical_store::{Calendar, CalendarStore, SubscriptionStore}; use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
@@ -104,6 +104,7 @@ impl Resource for CalendarResource {
type Prop = CalendarPropWrapper; type Prop = CalendarPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
if self.cal.subscription_url.is_none() { if self.cal.subscription_url.is_none() {
@@ -331,6 +332,7 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourc
type PathComponents = (String, String); // principal, calendar_id type PathComponents = (String, String); // principal, calendar_id
type Resource = CalendarResource; type Resource = CalendarResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -1,5 +1,5 @@
use super::methods::{get_event, put_event}; use super::methods::{get_event, put_event};
use crate::{principal::PrincipalResource, Error}; use crate::{Error, principal::PrincipalResource};
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use async_trait::async_trait; use async_trait::async_trait;
use derive_more::derive::{From, Into}; use derive_more::derive::{From, Into};
@@ -9,7 +9,7 @@ use rustical_dav::{
resource::{Resource, ResourceService}, resource::{Resource, ResourceService},
xml::Resourcetype, xml::Resourcetype,
}; };
use rustical_store::{auth::User, CalendarObject, CalendarStore}; use rustical_store::{CalendarObject, CalendarStore, auth::User};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
@@ -55,6 +55,7 @@ impl Resource for CalendarObjectResource {
type Prop = CalendarObjectPropWrapper; type Prop = CalendarObjectPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[]) Resourcetype(&[])
@@ -132,6 +133,7 @@ impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
type Resource = CalendarObjectResource; type Resource = CalendarObjectResource;
type MemberType = CalendarObjectResource; type MemberType = CalendarObjectResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -1,14 +1,14 @@
use crate::Error;
use crate::calendar::resource::CalendarResource; use crate::calendar::resource::CalendarResource;
use crate::principal::PrincipalResource; use crate::principal::PrincipalResource;
use crate::Error;
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use async_trait::async_trait; use async_trait::async_trait;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{Resource, ResourceService}; use rustical_dav::resource::{Resource, ResourceService};
use rustical_dav::xml::{Resourcetype, ResourcetypeInner}; use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::CalendarStore; use rustical_store::CalendarStore;
use rustical_store::auth::User;
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use std::sync::Arc; use std::sync::Arc;
@@ -28,6 +28,7 @@ impl Resource for CalendarSetResource {
type Prop = PrincipalPropWrapper; type Prop = PrincipalPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[ResourcetypeInner( Resourcetype(&[ResourcetypeInner(
@@ -78,6 +79,7 @@ impl<C: CalendarStore> ResourceService for CalendarSetResourceService<C> {
type MemberType = CalendarResource; type MemberType = CalendarResource;
type Resource = CalendarSetResource; type Resource = CalendarSetResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -1,16 +1,16 @@
use actix_web::HttpResponse;
use actix_web::dev::{HttpServiceFactory, ServiceResponse}; use actix_web::dev::{HttpServiceFactory, ServiceResponse};
use actix_web::http::header::{HeaderName, HeaderValue}; use actix_web::http::header::{HeaderName, HeaderValue};
use actix_web::http::{Method, StatusCode}; use actix_web::http::{Method, StatusCode};
use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers}; use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
use actix_web::web::{self, Data}; use actix_web::web::{self, Data};
use actix_web::HttpResponse;
use calendar::resource::CalendarResourceService; use calendar::resource::CalendarResourceService;
use calendar_object::resource::CalendarObjectResourceService; use calendar_object::resource::CalendarObjectResourceService;
use calendar_set::CalendarSetResourceService; use calendar_set::CalendarSetResourceService;
use principal::{PrincipalResource, PrincipalResourceService}; use principal::{PrincipalResource, PrincipalResourceService};
use rustical_dav::resource::{NamedRoute, ResourceService, ResourceServiceRoute}; use rustical_dav::resource::{NamedRoute, ResourceService, ResourceServiceRoute};
use rustical_dav::resources::RootResourceService; use rustical_dav::resources::RootResourceService;
use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider}; use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider, User};
use rustical_store::{AddressbookStore, CalendarStore, ContactBirthdayStore, SubscriptionStore}; use rustical_store::{AddressbookStore, CalendarStore, ContactBirthdayStore, SubscriptionStore};
use std::sync::Arc; use std::sync::Arc;
use subscription::subscription_resource; use subscription::subscription_resource;
@@ -62,7 +62,7 @@ pub fn caldav_service<
.app_data(Data::from(store.clone())) .app_data(Data::from(store.clone()))
.app_data(Data::from(birthday_store.clone())) .app_data(Data::from(birthday_store.clone()))
.app_data(Data::from(subscription_store)) .app_data(Data::from(subscription_store))
.service(RootResourceService::<PrincipalResource>::default().actix_resource()) .service(RootResourceService::<PrincipalResource, User>::default().actix_resource())
.service( .service(
web::scope("/principal").service( web::scope("/principal").service(
web::scope("/{principal}") web::scope("/{principal}")

View File

@@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::calendar_set::CalendarSetResource;
use crate::Error; use crate::Error;
use crate::calendar_set::CalendarSetResource;
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use async_trait::async_trait; use async_trait::async_trait;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
@@ -65,6 +65,7 @@ impl Resource for PrincipalResource {
type Prop = PrincipalPropWrapper; type Prop = PrincipalPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[ Resourcetype(&[
@@ -142,6 +143,7 @@ impl<AP: AuthenticationProvider> ResourceService for PrincipalResourceService<AP
type MemberType = CalendarSetResource; type MemberType = CalendarSetResource;
type Resource = PrincipalResource; type Resource = PrincipalResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -25,3 +25,4 @@ rustical_store = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
rustical_xml.workspace = true rustical_xml.workspace = true
uuid.workspace = true uuid.workspace = true
rustical_dav_push.workspace = true

View File

@@ -1,4 +1,4 @@
use crate::{principal::PrincipalResource, Error}; use crate::{Error, principal::PrincipalResource};
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use async_trait::async_trait; use async_trait::async_trait;
use derive_more::derive::{Constructor, From, Into}; use derive_more::derive::{Constructor, From, Into};
@@ -8,7 +8,7 @@ use rustical_dav::{
resource::{Resource, ResourceService}, resource::{Resource, ResourceService},
xml::Resourcetype, xml::Resourcetype,
}; };
use rustical_store::{auth::User, AddressObject, AddressbookStore}; use rustical_store::{AddressObject, AddressbookStore, auth::User};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use serde::Deserialize; use serde::Deserialize;
use std::sync::Arc; use std::sync::Arc;
@@ -51,6 +51,7 @@ impl Resource for AddressObjectResource {
type Prop = AddressObjectPropWrapper; type Prop = AddressObjectPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[]) Resourcetype(&[])
@@ -128,6 +129,7 @@ impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS>
type Resource = AddressObjectResource; type Resource = AddressObjectResource;
type MemberType = AddressObjectResource; type MemberType = AddressObjectResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -2,7 +2,7 @@ use crate::Error;
use actix_web::http::header; use actix_web::http::header;
use actix_web::web::{Data, Path}; use actix_web::web::{Data, Path};
use actix_web::{HttpRequest, HttpResponse}; use actix_web::{HttpRequest, HttpResponse};
use rustical_dav::push::PushRegister; use rustical_dav_push::register::PushRegister;
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::{AddressbookStore, Subscription, SubscriptionStore}; use rustical_store::{AddressbookStore, Subscription, SubscriptionStore};
use rustical_xml::XmlDocument; use rustical_xml::XmlDocument;

View File

@@ -2,21 +2,21 @@ use super::methods::mkcol::route_mkcol;
use super::methods::post::route_post; use super::methods::post::route_post;
use super::methods::report::route_report_addressbook; use super::methods::report::route_report_addressbook;
use super::prop::{SupportedAddressData, SupportedReportSet}; use super::prop::{SupportedAddressData, SupportedReportSet};
use crate::Error;
use crate::address_object::resource::AddressObjectResource; use crate::address_object::resource::AddressObjectResource;
use crate::principal::PrincipalResource; use crate::principal::PrincipalResource;
use crate::Error;
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use actix_web::http::Method; use actix_web::http::Method;
use actix_web::web; use actix_web::web;
use async_trait::async_trait; use async_trait::async_trait;
use derive_more::derive::{From, Into}; use derive_more::derive::{From, Into};
use rustical_dav::extensions::{ use rustical_dav::extensions::{
CommonPropertiesExtension, CommonPropertiesProp, DavPushExtension, DavPushExtensionProp, CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
SyncTokenExtension, SyncTokenExtensionProp,
}; };
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{Resource, ResourceService}; use rustical_dav::resource::{Resource, ResourceService};
use rustical_dav::xml::{Resourcetype, ResourcetypeInner}; use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
use rustical_store::auth::User; use rustical_store::auth::User;
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore}; use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
@@ -84,6 +84,7 @@ impl Resource for AddressbookResource {
type Prop = AddressbookPropWrapper; type Prop = AddressbookPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[ Resourcetype(&[
@@ -199,6 +200,7 @@ impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
type PathComponents = (String, String); // principal, addressbook_id type PathComponents = (String, String); // principal, addressbook_id
type Resource = AddressbookResource; type Resource = AddressbookResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -1,12 +1,12 @@
use actix_web::{ use actix_web::{
HttpResponse,
dev::{HttpServiceFactory, ServiceResponse}, dev::{HttpServiceFactory, ServiceResponse},
http::{ http::{
header::{HeaderName, HeaderValue},
Method, StatusCode, Method, StatusCode,
header::{HeaderName, HeaderValue},
}, },
middleware::{ErrorHandlerResponse, ErrorHandlers}, middleware::{ErrorHandlerResponse, ErrorHandlers},
web::{self, Data}, web::{self, Data},
HttpResponse,
}; };
use address_object::resource::AddressObjectResourceService; use address_object::resource::AddressObjectResourceService;
use addressbook::resource::AddressbookResourceService; use addressbook::resource::AddressbookResourceService;
@@ -15,8 +15,8 @@ use principal::{PrincipalResource, PrincipalResourceService};
use rustical_dav::resource::{NamedRoute, ResourceService}; use rustical_dav::resource::{NamedRoute, ResourceService};
use rustical_dav::resources::RootResourceService; use rustical_dav::resources::RootResourceService;
use rustical_store::{ use rustical_store::{
auth::{AuthenticationMiddleware, AuthenticationProvider},
AddressbookStore, SubscriptionStore, AddressbookStore, SubscriptionStore,
auth::{AuthenticationMiddleware, AuthenticationProvider, User},
}; };
use std::sync::Arc; use std::sync::Arc;
@@ -54,7 +54,7 @@ pub fn carddav_service<AP: AuthenticationProvider, A: AddressbookStore, S: Subsc
) )
.app_data(Data::from(store.clone())) .app_data(Data::from(store.clone()))
.app_data(Data::from(subscription_store)) .app_data(Data::from(subscription_store))
.service(RootResourceService::<PrincipalResource>::default().actix_resource()) .service(RootResourceService::<PrincipalResource, User>::default().actix_resource())
.service( .service(
web::scope("/principal").service( web::scope("/principal").service(
web::scope("/{principal}") web::scope("/{principal}")

View File

@@ -1,13 +1,13 @@
use crate::addressbook::resource::AddressbookResource;
use crate::Error; use crate::Error;
use crate::addressbook::resource::AddressbookResource;
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use async_trait::async_trait; use async_trait::async_trait;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp}; use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use rustical_dav::privileges::UserPrivilegeSet; use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{NamedRoute, Resource, ResourceService}; use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner}; use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_store::AddressbookStore; use rustical_store::AddressbookStore;
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use std::sync::Arc; use std::sync::Arc;
@@ -74,6 +74,7 @@ impl Resource for PrincipalResource {
type Prop = PrincipalPropWrapper; type Prop = PrincipalPropWrapper;
type Error = Error; type Error = Error;
type PrincipalResource = PrincipalResource; type PrincipalResource = PrincipalResource;
type Principal = User;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[ Resourcetype(&[
@@ -140,6 +141,7 @@ impl<A: AddressbookStore, AP: AuthenticationProvider> ResourceService
type MemberType = AddressbookResource; type MemberType = AddressbookResource;
type Resource = PrincipalResource; type Resource = PrincipalResource;
type Error = Error; type Error = Error;
type Principal = User;
async fn get_resource( async fn get_resource(
&self, &self,

View File

@@ -12,7 +12,6 @@ actix-web = { workspace = true }
async-trait = { workspace = true } async-trait = { workspace = true }
futures-util = { workspace = true } futures-util = { workspace = true }
quick-xml = { workspace = true } quick-xml = { workspace = true }
rustical_store = { workspace = true }
serde = { workspace = true } serde = { workspace = true }
thiserror = { workspace = true } thiserror = { workspace = true }
itertools = { workspace = true } itertools = { workspace = true }

View File

@@ -1,10 +1,10 @@
use crate::{ use crate::{
Principal,
privileges::UserPrivilegeSet, privileges::UserPrivilegeSet,
resource::{NamedRoute, Resource}, resource::{NamedRoute, Resource},
xml::{HrefElement, Resourcetype}, xml::{HrefElement, Resourcetype},
}; };
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use rustical_store::auth::User;
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)] #[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
@@ -31,7 +31,7 @@ pub trait CommonPropertiesExtension: Resource {
fn get_prop( fn get_prop(
&self, &self,
rmap: &ResourceMap, rmap: &ResourceMap,
user: &User, principal: &Self::Principal,
prop: &CommonPropertiesPropName, prop: &CommonPropertiesPropName,
) -> Result<CommonPropertiesProp, <Self as Resource>::Error> { ) -> Result<CommonPropertiesProp, <Self as Resource>::Error> {
Ok(match prop { Ok(match prop {
@@ -40,13 +40,13 @@ pub trait CommonPropertiesExtension: Resource {
} }
CommonPropertiesPropName::CurrentUserPrincipal => { CommonPropertiesPropName::CurrentUserPrincipal => {
CommonPropertiesProp::CurrentUserPrincipal( CommonPropertiesProp::CurrentUserPrincipal(
Self::PrincipalResource::get_url(rmap, [&user.id]) Self::PrincipalResource::get_url(rmap, [&principal.get_id()])
.unwrap() .unwrap()
.into(), .into(),
) )
} }
CommonPropertiesPropName::CurrentUserPrivilegeSet => { CommonPropertiesPropName::CurrentUserPrivilegeSet => {
CommonPropertiesProp::CurrentUserPrivilegeSet(self.get_user_privileges(user)?) CommonPropertiesProp::CurrentUserPrivilegeSet(self.get_user_privileges(principal)?)
} }
CommonPropertiesPropName::Owner => { CommonPropertiesPropName::Owner => {
CommonPropertiesProp::Owner(self.get_owner().map(|owner| { CommonPropertiesProp::Owner(self.get_owner().map(|owner| {

View File

@@ -1,7 +1,5 @@
mod common; mod common;
mod davpush;
mod synctoken; mod synctoken;
pub use common::*; pub use common::*;
pub use davpush::*;
pub use synctoken::*; pub use synctoken::*;

View File

@@ -3,9 +3,13 @@ pub mod error;
pub mod extensions; pub mod extensions;
pub mod namespace; pub mod namespace;
pub mod privileges; pub mod privileges;
pub mod push;
pub mod resource; pub mod resource;
pub mod resources; pub mod resources;
pub mod xml; pub mod xml;
use actix_web::FromRequest;
pub use error::Error; pub use error::Error;
pub trait Principal: std::fmt::Debug + Clone + FromRequest + 'static {
fn get_id(&self) -> &str;
}

View File

@@ -1,7 +0,0 @@
mod prop;
mod push_notifier;
mod push_register;
pub use prop::*;
pub use push_notifier::push_notifier;
pub use push_register::*;

View File

@@ -1,16 +1,15 @@
use crate::Error;
use crate::privileges::UserPrivilege; use crate::privileges::UserPrivilege;
use crate::resource::Resource; use crate::resource::Resource;
use crate::resource::ResourceService; use crate::resource::ResourceService;
use crate::Error; use actix_web::HttpRequest;
use actix_web::HttpResponse;
use actix_web::Responder;
use actix_web::http::header::IfMatch; use actix_web::http::header::IfMatch;
use actix_web::http::header::IfNoneMatch; use actix_web::http::header::IfNoneMatch;
use actix_web::web; use actix_web::web;
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::web::Path; use actix_web::web::Path;
use actix_web::HttpRequest;
use actix_web::HttpResponse;
use actix_web::Responder;
use rustical_store::auth::User;
use tracing::instrument; use tracing::instrument;
use tracing_actix_web::RootSpan; use tracing_actix_web::RootSpan;
@@ -18,7 +17,7 @@ use tracing_actix_web::RootSpan;
pub async fn route_delete<R: ResourceService>( pub async fn route_delete<R: ResourceService>(
path: Path<R::PathComponents>, path: Path<R::PathComponents>,
req: HttpRequest, req: HttpRequest,
user: User, principal: R::Principal,
resource_service: Data<R>, resource_service: Data<R>,
root_span: RootSpan, root_span: RootSpan,
if_match: web::Header<IfMatch>, if_match: web::Header<IfMatch>,
@@ -32,7 +31,7 @@ pub async fn route_delete<R: ResourceService>(
let resource = resource_service.get_resource(&path).await?; let resource = resource_service.get_resource(&path).await?;
let privileges = resource.get_user_privileges(&user)?; let privileges = resource.get_user_privileges(&principal)?;
if !privileges.has(&UserPrivilege::Write) { if !privileges.has(&UserPrivilege::Write) {
return Err(Error::Unauthorized.into()); return Err(Error::Unauthorized.into());
} }

View File

@@ -1,3 +1,4 @@
use crate::Error;
use crate::depth_header::Depth; use crate::depth_header::Depth;
use crate::privileges::UserPrivilege; use crate::privileges::UserPrivilege;
use crate::resource::Resource; use crate::resource::Resource;
@@ -6,11 +7,9 @@ use crate::xml::MultistatusElement;
use crate::xml::PropElement; use crate::xml::PropElement;
use crate::xml::PropfindElement; use crate::xml::PropfindElement;
use crate::xml::PropfindType; use crate::xml::PropfindType;
use crate::Error; use actix_web::HttpRequest;
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::web::Path; use actix_web::web::Path;
use actix_web::HttpRequest;
use rustical_store::auth::User;
use rustical_xml::XmlDocument; use rustical_xml::XmlDocument;
use tracing::instrument; use tracing::instrument;
use tracing_actix_web::RootSpan; use tracing_actix_web::RootSpan;
@@ -21,7 +20,7 @@ pub(crate) async fn route_propfind<R: ResourceService>(
path: Path<R::PathComponents>, path: Path<R::PathComponents>,
body: String, body: String,
req: HttpRequest, req: HttpRequest,
user: User, user: R::Principal,
depth: Depth, depth: Depth,
root_span: RootSpan, root_span: RootSpan,
resource_service: Data<R>, resource_service: Data<R>,

View File

@@ -1,15 +1,14 @@
use crate::Error;
use crate::privileges::UserPrivilege; use crate::privileges::UserPrivilege;
use crate::resource::Resource; use crate::resource::Resource;
use crate::resource::ResourceService; use crate::resource::ResourceService;
use crate::xml::multistatus::{PropstatElement, PropstatWrapper, ResponseElement};
use crate::xml::MultistatusElement; use crate::xml::MultistatusElement;
use crate::xml::TagList; use crate::xml::TagList;
use crate::Error; use crate::xml::multistatus::{PropstatElement, PropstatWrapper, ResponseElement};
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::{web::Path, HttpRequest}; use actix_web::{HttpRequest, web::Path};
use quick_xml::name::Namespace; use quick_xml::name::Namespace;
use rustical_store::auth::User;
use rustical_xml::EnumUnitVariants; use rustical_xml::EnumUnitVariants;
use rustical_xml::Unparsed; use rustical_xml::Unparsed;
use rustical_xml::XmlDeserialize; use rustical_xml::XmlDeserialize;
@@ -69,7 +68,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
path: Path<R::PathComponents>, path: Path<R::PathComponents>,
body: String, body: String,
req: HttpRequest, req: HttpRequest,
user: User, principal: R::Principal,
root_span: RootSpan, root_span: RootSpan,
resource_service: Data<R>, resource_service: Data<R>,
) -> Result<MultistatusElement<String, String>, R::Error> { ) -> Result<MultistatusElement<String, String>, R::Error> {
@@ -81,7 +80,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
) = XmlDocument::parse_str(&body).map_err(Error::XmlError)?; ) = XmlDocument::parse_str(&body).map_err(Error::XmlError)?;
let mut resource = resource_service.get_resource(&path).await?; let mut resource = resource_service.get_resource(&path).await?;
let privileges = resource.get_user_privileges(&user)?; let privileges = resource.get_user_privileges(&principal)?;
if !privileges.has(&UserPrivilege::Write) { if !privileges.has(&UserPrivilege::Write) {
return Err(Error::Unauthorized.into()); return Err(Error::Unauthorized.into());
} }
@@ -131,7 +130,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
} }
} }
Operation::Remove(remove_el) => { Operation::Remove(remove_el) => {
let propname = remove_el.prop.0 .0; let propname = remove_el.prop.0.0;
match <<R::Resource as Resource>::Prop as EnumUnitVariants>::UnitVariants::from_str( match <<R::Resource as Resource>::Prop as EnumUnitVariants>::UnitVariants::from_str(
&propname, &propname,
) { ) {

View File

@@ -1,15 +1,14 @@
use crate::privileges::UserPrivilegeSet; use crate::privileges::UserPrivilegeSet;
use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper};
use crate::xml::Resourcetype; use crate::xml::Resourcetype;
use crate::xml::{multistatus::ResponseElement, TagList}; use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper};
use crate::Error; use crate::xml::{TagList, multistatus::ResponseElement};
use crate::{Error, Principal};
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use actix_web::http::header::{EntityTag, IfMatch, IfNoneMatch}; use actix_web::http::header::{EntityTag, IfMatch, IfNoneMatch};
use actix_web::{http::StatusCode, ResponseError}; use actix_web::{ResponseError, http::StatusCode};
use itertools::Itertools; use itertools::Itertools;
use quick_xml::name::Namespace; use quick_xml::name::Namespace;
pub use resource_service::ResourceService; pub use resource_service::ResourceService;
use rustical_store::auth::User;
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
use std::str::FromStr; use std::str::FromStr;
@@ -28,6 +27,7 @@ pub trait Resource: Clone + 'static {
type Prop: ResourceProp + PartialEq + Clone + EnumVariants + EnumUnitVariants; type Prop: ResourceProp + PartialEq + Clone + EnumVariants + EnumUnitVariants;
type Error: ResponseError + From<crate::Error>; type Error: ResponseError + From<crate::Error>;
type PrincipalResource: Resource + NamedRoute; type PrincipalResource: Resource + NamedRoute;
type Principal: Principal;
fn get_resourcetype(&self) -> Resourcetype; fn get_resourcetype(&self) -> Resourcetype;
@@ -38,7 +38,7 @@ pub trait Resource: Clone + 'static {
fn get_prop( fn get_prop(
&self, &self,
rmap: &ResourceMap, rmap: &ResourceMap,
user: &User, principal: &Self::Principal,
prop: &<Self::Prop as EnumUnitVariants>::UnitVariants, prop: &<Self::Prop as EnumUnitVariants>::UnitVariants,
) -> Result<Self::Prop, Self::Error>; ) -> Result<Self::Prop, Self::Error>;
@@ -93,13 +93,16 @@ pub trait Resource: Clone + 'static {
} }
} }
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error>; fn get_user_privileges(
&self,
principal: &Self::Principal,
) -> Result<UserPrivilegeSet, Self::Error>;
fn propfind( fn propfind(
&self, &self,
path: &str, path: &str,
props: &[&str], props: &[&str],
user: &User, principal: &Self::Principal,
rmap: &ResourceMap, rmap: &ResourceMap,
) -> Result<ResponseElement<Self::Prop>, Self::Error> { ) -> Result<ResponseElement<Self::Prop>, Self::Error> {
let mut props = props.to_vec(); let mut props = props.to_vec();
@@ -152,7 +155,7 @@ pub trait Resource: Clone + 'static {
let prop_responses = valid_props let prop_responses = valid_props
.into_iter() .into_iter()
.map(|prop| self.get_prop(rmap, user, &prop)) .map(|prop| self.get_prop(rmap, principal, &prop))
.collect::<Result<Vec<_>, Self::Error>>()?; .collect::<Result<Vec<_>, Self::Error>>()?;
let mut propstats = vec![PropstatWrapper::Normal(PropstatElement { let mut propstats = vec![PropstatWrapper::Normal(PropstatElement {

View File

@@ -2,20 +2,23 @@ use actix_web::dev::{AppService, HttpServiceFactory};
use actix_web::error::UrlGenerationError; use actix_web::error::UrlGenerationError;
use actix_web::test::TestRequest; use actix_web::test::TestRequest;
use actix_web::web::Data; use actix_web::web::Data;
use actix_web::{dev::ResourceMap, http::Method, web, ResponseError}; use actix_web::{ResponseError, dev::ResourceMap, http::Method, web};
use async_trait::async_trait; use async_trait::async_trait;
use serde::Deserialize; use serde::Deserialize;
use std::str::FromStr; use std::str::FromStr;
use super::methods::{route_delete, route_propfind, route_proppatch}; use crate::Principal;
use super::Resource; use super::Resource;
use super::methods::{route_delete, route_propfind, route_proppatch};
#[async_trait(?Send)] #[async_trait(?Send)]
pub trait ResourceService: Sized + 'static { pub trait ResourceService: Sized + 'static {
type MemberType: Resource<Error = Self::Error>; type MemberType: Resource<Error = Self::Error, Principal = Self::Principal>;
type PathComponents: for<'de> Deserialize<'de> + Sized + Clone + 'static; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String) type PathComponents: for<'de> Deserialize<'de> + Sized + Clone + 'static; // defines how the resource URI maps to parameters, i.e. /{principal}/{calendar} -> (String, String)
type Resource: Resource<Error = Self::Error>; type Resource: Resource<Error = Self::Error, Principal = Self::Principal>;
type Error: ResponseError + From<crate::Error>; type Error: ResponseError + From<crate::Error>;
type Principal: Principal;
async fn get_members( async fn get_members(
&self, &self,

View File

@@ -1,3 +1,4 @@
use crate::Principal;
use crate::extensions::{ use crate::extensions::{
CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName, CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName,
}; };
@@ -6,22 +7,22 @@ use crate::resource::{NamedRoute, Resource, ResourceService};
use crate::xml::{Resourcetype, ResourcetypeInner}; use crate::xml::{Resourcetype, ResourcetypeInner};
use actix_web::dev::ResourceMap; use actix_web::dev::ResourceMap;
use async_trait::async_trait; use async_trait::async_trait;
use rustical_store::auth::User;
use std::marker::PhantomData; use std::marker::PhantomData;
#[derive(Clone)] #[derive(Clone)]
pub struct RootResource<PR: Resource>(PhantomData<PR>); pub struct RootResource<PR: Resource, P: Principal>(PhantomData<PR>, PhantomData<P>);
impl<PR: Resource> Default for RootResource<PR> { impl<PR: Resource, P: Principal> Default for RootResource<PR, P> {
fn default() -> Self { fn default() -> Self {
Self(Default::default()) Self(PhantomData, PhantomData)
} }
} }
impl<PR: Resource + NamedRoute> Resource for RootResource<PR> { impl<PR: Resource + NamedRoute, P: Principal> Resource for RootResource<PR, P> {
type Prop = CommonPropertiesProp; type Prop = CommonPropertiesProp;
type Error = PR::Error; type Error = PR::Error;
type PrincipalResource = PR; type PrincipalResource = PR;
type Principal = P;
fn get_resourcetype(&self) -> Resourcetype { fn get_resourcetype(&self) -> Resourcetype {
Resourcetype(&[ResourcetypeInner( Resourcetype(&[ResourcetypeInner(
@@ -33,34 +34,37 @@ impl<PR: Resource + NamedRoute> Resource for RootResource<PR> {
fn get_prop( fn get_prop(
&self, &self,
rmap: &ResourceMap, rmap: &ResourceMap,
user: &User, user: &P,
prop: &CommonPropertiesPropName, prop: &CommonPropertiesPropName,
) -> Result<Self::Prop, Self::Error> { ) -> Result<Self::Prop, Self::Error> {
CommonPropertiesExtension::get_prop(self, rmap, user, prop) CommonPropertiesExtension::get_prop(self, rmap, user, prop)
} }
fn get_user_privileges(&self, _user: &User) -> Result<UserPrivilegeSet, Self::Error> { fn get_user_privileges(&self, _user: &P) -> Result<UserPrivilegeSet, Self::Error> {
Ok(UserPrivilegeSet::all()) Ok(UserPrivilegeSet::all())
} }
} }
#[derive(Clone)] #[derive(Clone)]
pub struct RootResourceService<PR: Resource>(PhantomData<PR>); pub struct RootResourceService<PR: Resource, P: Principal>(PhantomData<PR>, PhantomData<P>);
impl<PR: Resource> Default for RootResourceService<PR> { impl<PR: Resource, P: Principal> Default for RootResourceService<PR, P> {
fn default() -> Self { fn default() -> Self {
Self(PhantomData) Self(PhantomData, PhantomData)
} }
} }
#[async_trait(?Send)] #[async_trait(?Send)]
impl<PR: Resource + NamedRoute> ResourceService for RootResourceService<PR> { impl<PR: Resource<Principal = P> + NamedRoute, P: Principal> ResourceService
for RootResourceService<PR, P>
{
type PathComponents = (); type PathComponents = ();
type MemberType = PR; type MemberType = PR;
type Resource = RootResource<PR>; type Resource = RootResource<PR, P>;
type Error = PR::Error; type Error = PR::Error;
type Principal = P;
async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> { async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> {
Ok(RootResource::<PR>::default()) Ok(RootResource::<PR, P>::default())
} }
} }

View File

@@ -0,0 +1,25 @@
[package]
name = "rustical_dav_push"
version.workspace = true
edition.workspace = true
description.workspace = true
repository.workspace = true
publish = false
[dependencies]
rustical_xml.workspace = true
actix-web = { workspace = true }
async-trait = { workspace = true }
futures-util = { workspace = true }
quick-xml = { workspace = true }
serde = { workspace = true }
thiserror = { workspace = true }
itertools = { workspace = true }
log = { workspace = true }
derive_more = { workspace = true }
tracing = { workspace = true }
tracing-actix-web = { workspace = true }
reqwest.workspace = true
tokio.workspace = true
rustical_dav.workspace = true
rustical_store.workspace = true

View File

@@ -1,4 +1,4 @@
use crate::push::Transports; use crate::Transports;
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}; use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)] #[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
@@ -6,9 +6,9 @@ use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}
pub enum DavPushExtensionProp { pub enum DavPushExtensionProp {
// WebDav Push // WebDav Push
#[xml(skip_deserializing)] #[xml(skip_deserializing)]
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
Transports(Transports), Transports(Transports),
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
Topic(String), Topic(String),
} }
@@ -18,7 +18,7 @@ pub trait DavPushExtension {
fn get_prop( fn get_prop(
&self, &self,
prop: &DavPushExtensionPropName, prop: &DavPushExtensionPropName,
) -> Result<DavPushExtensionProp, crate::Error> { ) -> Result<DavPushExtensionProp, rustical_dav::Error> {
Ok(match &prop { Ok(match &prop {
DavPushExtensionPropName::Transports => { DavPushExtensionPropName::Transports => {
DavPushExtensionProp::Transports(Default::default()) DavPushExtensionProp::Transports(Default::default())
@@ -27,11 +27,11 @@ pub trait DavPushExtension {
}) })
} }
fn set_prop(&self, _prop: DavPushExtensionProp) -> Result<(), crate::Error> { fn set_prop(&self, _prop: DavPushExtensionProp) -> Result<(), rustical_dav::Error> {
Err(crate::Error::PropReadOnly) Err(rustical_dav::Error::PropReadOnly)
} }
fn remove_prop(&self, _prop: &DavPushExtensionPropName) -> Result<(), crate::Error> { fn remove_prop(&self, _prop: &DavPushExtensionPropName) -> Result<(), rustical_dav::Error> {
Err(crate::Error::PropReadOnly) Err(rustical_dav::Error::PropReadOnly)
} }
} }

View File

@@ -0,0 +1,7 @@
mod extension;
pub mod notifier;
mod prop;
pub mod register;
pub use extension::*;
pub use prop::*;

View File

@@ -1,5 +1,5 @@
use crate::xml::multistatus::PropstatElement;
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use rustical_dav::xml::multistatus::PropstatElement;
use rustical_store::{CollectionOperation, CollectionOperationType, SubscriptionStore}; use rustical_store::{CollectionOperation, CollectionOperationType, SubscriptionStore};
use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot}; use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
use std::sync::Arc; use std::sync::Arc;
@@ -8,17 +8,20 @@ use tracing::{error, info, warn};
#[derive(XmlSerialize, Debug)] #[derive(XmlSerialize, Debug)]
struct PushMessageProp { struct PushMessageProp {
#[xml(ns = "crate::namespace::NS_DAV")] #[xml(ns = "rustical_dav::namespace::NS_DAV")]
topic: String, topic: String,
#[xml(ns = "crate::namespace::NS_DAV")] #[xml(ns = "rustical_dav::namespace::NS_DAV")]
sync_token: Option<String>, sync_token: Option<String>,
} }
#[derive(XmlSerialize, XmlRootTag, Debug)] #[derive(XmlSerialize, XmlRootTag, Debug)]
#[xml(root = b"push-message", ns = "crate::namespace::NS_DAVPUSH")] #[xml(root = b"push-message", ns = "rustical_dav::namespace::NS_DAVPUSH")]
#[xml(ns_prefix(crate::namespace::NS_DAVPUSH = b"", crate::namespace::NS_DAV = b"D",))] #[xml(ns_prefix(
rustical_dav::namespace::NS_DAVPUSH = b"",
rustical_dav::namespace::NS_DAV = b"D",
))]
struct PushMessage { struct PushMessage {
#[xml(ns = "crate::namespace::NS_DAV")] #[xml(ns = "rustical_dav::namespace::NS_DAV")]
propstat: PropstatElement<PushMessageProp>, propstat: PropstatElement<PushMessageProp>,
} }
@@ -87,7 +90,10 @@ pub async fn push_notifier(
error!("{err}"); error!("{err}");
} }
} else { } else {
warn!("Not sending a push notification to {} since it's not allowed in dav_push::allowed_push_servers", push_resource); warn!(
"Not sending a push notification to {} since it's not allowed in dav_push::allowed_push_servers",
push_resource
);
} }
} }
} }

View File

@@ -2,7 +2,7 @@ use rustical_xml::XmlSerialize;
#[derive(Debug, Clone, XmlSerialize, PartialEq)] #[derive(Debug, Clone, XmlSerialize, PartialEq)]
pub enum Transport { pub enum Transport {
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
WebPush, WebPush,
} }

View File

@@ -3,23 +3,23 @@ use rustical_xml::{XmlDeserialize, XmlRootTag};
#[derive(XmlDeserialize, Clone, Debug, PartialEq)] #[derive(XmlDeserialize, Clone, Debug, PartialEq)]
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "crate::namespace::NS_DAVPUSH")]
pub struct WebPushSubscription { pub struct WebPushSubscription {
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub push_resource: String, pub push_resource: String,
} }
#[derive(XmlDeserialize, Clone, Debug, PartialEq)] #[derive(XmlDeserialize, Clone, Debug, PartialEq)]
pub struct SubscriptionElement { pub struct SubscriptionElement {
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub web_push_subscription: WebPushSubscription, pub web_push_subscription: WebPushSubscription,
} }
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)] #[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)]
#[xml(root = b"push-register")] #[xml(root = b"push-register")]
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub struct PushRegister { pub struct PushRegister {
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub subscription: SubscriptionElement, pub subscription: SubscriptionElement,
#[xml(ns = "crate::namespace::NS_DAVPUSH")] #[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
pub expires: Option<String>, pub expires: Option<String>,
} }

View File

@@ -10,9 +10,9 @@ use actix_web::{
}; };
use error::OidcError; use error::OidcError;
use openidconnect::{ use openidconnect::{
AuthenticationFlow, AuthorizationCode, CsrfToken, EmptyAdditionalClaims, EndpointMaybeSet, AuthenticationFlow, AuthorizationCode, CsrfToken, EndpointMaybeSet, EndpointNotSet,
EndpointNotSet, EndpointSet, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, EndpointSet, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier,
PkceCodeVerifier, RedirectUrl, TokenResponse, UserInfoClaims, RedirectUrl, TokenResponse, UserInfoClaims,
core::{CoreClient, CoreGenderClaim, CoreProviderMetadata, CoreResponseType}, core::{CoreClient, CoreGenderClaim, CoreProviderMetadata, CoreResponseType},
}; };
use rustical_store::auth::{AuthenticationProvider, User, user::PrincipalType::Individual}; use rustical_store::auth::{AuthenticationProvider, User, user::PrincipalType::Individual};

View File

@@ -30,6 +30,7 @@ tokio.workspace = true
rand.workspace = true rand.workspace = true
uuid.workspace = true uuid.workspace = true
clap.workspace = true clap.workspace = true
rustical_dav.workspace = true
[dev-dependencies] [dev-dependencies]
rstest = { workspace = true } rstest = { workspace = true }

View File

@@ -81,6 +81,12 @@ impl User {
} }
} }
impl rustical_dav::Principal for User {
fn get_id(&self) -> &str {
&self.id
}
}
#[derive(Clone, Debug, Display)] #[derive(Clone, Debug, Display)]
pub struct UnauthorizedError; pub struct UnauthorizedError;

View File

@@ -9,7 +9,7 @@ use commands::{cmd_gen_config, cmd_pwhash};
use config::{DataStoreConfig, SqliteDataStoreConfig}; use config::{DataStoreConfig, SqliteDataStoreConfig};
use figment::Figment; use figment::Figment;
use figment::providers::{Env, Format, Toml}; use figment::providers::{Env, Format, Toml};
use rustical_dav::push::push_notifier; use rustical_dav_push::notifier::push_notifier;
use rustical_frontend::nextcloud_login::NextcloudFlows; use rustical_frontend::nextcloud_login::NextcloudFlows;
use rustical_store::auth::TomlPrincipalStore; use rustical_store::auth::TomlPrincipalStore;
use rustical_store::{AddressbookStore, CalendarStore, CollectionOperation, SubscriptionStore}; use rustical_store::{AddressbookStore, CalendarStore, CollectionOperation, SubscriptionStore};
@@ -150,51 +150,51 @@ mod tests {
} }
async fn get_principal( async fn get_principal(
&self, &self,
id: &str, _id: &str,
) -> Result<Option<rustical_store::auth::User>, rustical_store::Error> { ) -> Result<Option<rustical_store::auth::User>, rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }
async fn remove_principal(&self, id: &str) -> Result<(), rustical_store::Error> { async fn remove_principal(&self, _id: &str) -> Result<(), rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }
async fn validate_password( async fn validate_password(
&self, &self,
user_id: &str, _user_id: &str,
password: &str, _password: &str,
) -> Result<Option<rustical_store::auth::User>, rustical_store::Error> { ) -> Result<Option<rustical_store::auth::User>, rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }
async fn validate_app_token( async fn validate_app_token(
&self, &self,
user_id: &str, _user_id: &str,
token: &str, _token: &str,
) -> Result<Option<rustical_store::auth::User>, rustical_store::Error> { ) -> Result<Option<rustical_store::auth::User>, rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }
async fn add_app_token( async fn add_app_token(
&self, &self,
user_id: &str, _user_id: &str,
name: String, _name: String,
token: String, _token: String,
) -> Result<String, rustical_store::Error> { ) -> Result<String, rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }
async fn remove_app_token( async fn remove_app_token(
&self, &self,
user_id: &str, _user_id: &str,
token_id: &str, _token_id: &str,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }
async fn insert_principal( async fn insert_principal(
&self, &self,
user: rustical_store::auth::User, _user: rustical_store::auth::User,
) -> Result<(), rustical_store::Error> { ) -> Result<(), rustical_store::Error> {
Err(rustical_store::Error::Other(anyhow!("Not implemented"))) Err(rustical_store::Error::Other(anyhow!("Not implemented")))
} }