mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 22:52:22 +00:00
dav: Make reusable for other projects
This commit is contained in:
@@ -27,3 +27,4 @@ chrono-tz = { workspace = true }
|
||||
sha2 = { workspace = true }
|
||||
rustical_xml.workspace = true
|
||||
uuid.workspace = true
|
||||
rustical_dav_push.workspace = true
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use crate::Error;
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use actix_web::http::header;
|
||||
use actix_web::web::{Data, Path};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use rustical_dav::privileges::UserPrivilege;
|
||||
use rustical_dav::push::PushRegister;
|
||||
use rustical_dav::resource::Resource;
|
||||
use rustical_dav_push::register::PushRegister;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{CalendarStore, Subscription, SubscriptionStore};
|
||||
use rustical_xml::XmlDocument;
|
||||
|
||||
@@ -2,9 +2,9 @@ use super::methods::mkcalendar::route_mkcalendar;
|
||||
use super::methods::post::route_post;
|
||||
use super::methods::report::route_report_calendar;
|
||||
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
|
||||
use crate::Error;
|
||||
use crate::calendar_object::resource::CalendarObjectResource;
|
||||
use crate::principal::PrincipalResource;
|
||||
use crate::Error;
|
||||
use actix_web::dev::ResourceMap;
|
||||
use actix_web::http::Method;
|
||||
use actix_web::web;
|
||||
@@ -12,12 +12,12 @@ use async_trait::async_trait;
|
||||
use chrono::{DateTime, Utc};
|
||||
use derive_more::derive::{From, Into};
|
||||
use rustical_dav::extensions::{
|
||||
CommonPropertiesExtension, CommonPropertiesProp, DavPushExtension, DavPushExtensionProp,
|
||||
SyncTokenExtension, SyncTokenExtensionProp,
|
||||
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
||||
};
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{Resource, ResourceService};
|
||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::calendar::CalDateTime;
|
||||
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
|
||||
@@ -104,6 +104,7 @@ impl Resource for CalendarResource {
|
||||
type Prop = CalendarPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
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 Resource = CalendarResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
use super::methods::{get_event, put_event};
|
||||
use crate::{principal::PrincipalResource, Error};
|
||||
use crate::{Error, principal::PrincipalResource};
|
||||
use actix_web::dev::ResourceMap;
|
||||
use async_trait::async_trait;
|
||||
use derive_more::derive::{From, Into};
|
||||
@@ -9,7 +9,7 @@ use rustical_dav::{
|
||||
resource::{Resource, ResourceService},
|
||||
xml::Resourcetype,
|
||||
};
|
||||
use rustical_store::{auth::User, CalendarObject, CalendarStore};
|
||||
use rustical_store::{CalendarObject, CalendarStore, auth::User};
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
@@ -55,6 +55,7 @@ impl Resource for CalendarObjectResource {
|
||||
type Prop = CalendarObjectPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[])
|
||||
@@ -132,6 +133,7 @@ impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
|
||||
type Resource = CalendarObjectResource;
|
||||
type MemberType = CalendarObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use crate::Error;
|
||||
use crate::calendar::resource::CalendarResource;
|
||||
use crate::principal::PrincipalResource;
|
||||
use crate::Error;
|
||||
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::{Resource, ResourceService};
|
||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::CalendarStore;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -28,6 +28,7 @@ impl Resource for CalendarSetResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[ResourcetypeInner(
|
||||
@@ -78,6 +79,7 @@ impl<C: CalendarStore> ResourceService for CalendarSetResourceService<C> {
|
||||
type MemberType = CalendarResource;
|
||||
type Resource = CalendarSetResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
use actix_web::HttpResponse;
|
||||
use actix_web::dev::{HttpServiceFactory, ServiceResponse};
|
||||
use actix_web::http::header::{HeaderName, HeaderValue};
|
||||
use actix_web::http::{Method, StatusCode};
|
||||
use actix_web::middleware::{ErrorHandlerResponse, ErrorHandlers};
|
||||
use actix_web::web::{self, Data};
|
||||
use actix_web::HttpResponse;
|
||||
use calendar::resource::CalendarResourceService;
|
||||
use calendar_object::resource::CalendarObjectResourceService;
|
||||
use calendar_set::CalendarSetResourceService;
|
||||
use principal::{PrincipalResource, PrincipalResourceService};
|
||||
use rustical_dav::resource::{NamedRoute, ResourceService, ResourceServiceRoute};
|
||||
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 std::sync::Arc;
|
||||
use subscription::subscription_resource;
|
||||
@@ -62,7 +62,7 @@ pub fn caldav_service<
|
||||
.app_data(Data::from(store.clone()))
|
||||
.app_data(Data::from(birthday_store.clone()))
|
||||
.app_data(Data::from(subscription_store))
|
||||
.service(RootResourceService::<PrincipalResource>::default().actix_resource())
|
||||
.service(RootResourceService::<PrincipalResource, User>::default().actix_resource())
|
||||
.service(
|
||||
web::scope("/principal").service(
|
||||
web::scope("/{principal}")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::calendar_set::CalendarSetResource;
|
||||
use crate::Error;
|
||||
use crate::calendar_set::CalendarSetResource;
|
||||
use actix_web::dev::ResourceMap;
|
||||
use async_trait::async_trait;
|
||||
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
||||
@@ -65,6 +65,7 @@ impl Resource for PrincipalResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[
|
||||
@@ -142,6 +143,7 @@ impl<AP: AuthenticationProvider> ResourceService for PrincipalResourceService<AP
|
||||
type MemberType = CalendarSetResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -25,3 +25,4 @@ rustical_store = { workspace = true }
|
||||
chrono = { workspace = true }
|
||||
rustical_xml.workspace = true
|
||||
uuid.workspace = true
|
||||
rustical_dav_push.workspace = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::{principal::PrincipalResource, Error};
|
||||
use crate::{Error, principal::PrincipalResource};
|
||||
use actix_web::dev::ResourceMap;
|
||||
use async_trait::async_trait;
|
||||
use derive_more::derive::{Constructor, From, Into};
|
||||
@@ -8,7 +8,7 @@ use rustical_dav::{
|
||||
resource::{Resource, ResourceService},
|
||||
xml::Resourcetype,
|
||||
};
|
||||
use rustical_store::{auth::User, AddressObject, AddressbookStore};
|
||||
use rustical_store::{AddressObject, AddressbookStore, auth::User};
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
use serde::Deserialize;
|
||||
use std::sync::Arc;
|
||||
@@ -51,6 +51,7 @@ impl Resource for AddressObjectResource {
|
||||
type Prop = AddressObjectPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[])
|
||||
@@ -128,6 +129,7 @@ impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS>
|
||||
type Resource = AddressObjectResource;
|
||||
type MemberType = AddressObjectResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -2,7 +2,7 @@ use crate::Error;
|
||||
use actix_web::http::header;
|
||||
use actix_web::web::{Data, Path};
|
||||
use actix_web::{HttpRequest, HttpResponse};
|
||||
use rustical_dav::push::PushRegister;
|
||||
use rustical_dav_push::register::PushRegister;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{AddressbookStore, Subscription, SubscriptionStore};
|
||||
use rustical_xml::XmlDocument;
|
||||
|
||||
@@ -2,21 +2,21 @@ use super::methods::mkcol::route_mkcol;
|
||||
use super::methods::post::route_post;
|
||||
use super::methods::report::route_report_addressbook;
|
||||
use super::prop::{SupportedAddressData, SupportedReportSet};
|
||||
use crate::Error;
|
||||
use crate::address_object::resource::AddressObjectResource;
|
||||
use crate::principal::PrincipalResource;
|
||||
use crate::Error;
|
||||
use actix_web::dev::ResourceMap;
|
||||
use actix_web::http::Method;
|
||||
use actix_web::web;
|
||||
use async_trait::async_trait;
|
||||
use derive_more::derive::{From, Into};
|
||||
use rustical_dav::extensions::{
|
||||
CommonPropertiesExtension, CommonPropertiesProp, DavPushExtension, DavPushExtensionProp,
|
||||
SyncTokenExtension, SyncTokenExtensionProp,
|
||||
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
|
||||
};
|
||||
use rustical_dav::privileges::UserPrivilegeSet;
|
||||
use rustical_dav::resource::{Resource, ResourceService};
|
||||
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
|
||||
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
|
||||
use rustical_store::auth::User;
|
||||
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
@@ -84,6 +84,7 @@ impl Resource for AddressbookResource {
|
||||
type Prop = AddressbookPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[
|
||||
@@ -199,6 +200,7 @@ impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
|
||||
type PathComponents = (String, String); // principal, addressbook_id
|
||||
type Resource = AddressbookResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use actix_web::{
|
||||
HttpResponse,
|
||||
dev::{HttpServiceFactory, ServiceResponse},
|
||||
http::{
|
||||
header::{HeaderName, HeaderValue},
|
||||
Method, StatusCode,
|
||||
header::{HeaderName, HeaderValue},
|
||||
},
|
||||
middleware::{ErrorHandlerResponse, ErrorHandlers},
|
||||
web::{self, Data},
|
||||
HttpResponse,
|
||||
};
|
||||
use address_object::resource::AddressObjectResourceService;
|
||||
use addressbook::resource::AddressbookResourceService;
|
||||
@@ -15,8 +15,8 @@ use principal::{PrincipalResource, PrincipalResourceService};
|
||||
use rustical_dav::resource::{NamedRoute, ResourceService};
|
||||
use rustical_dav::resources::RootResourceService;
|
||||
use rustical_store::{
|
||||
auth::{AuthenticationMiddleware, AuthenticationProvider},
|
||||
AddressbookStore, SubscriptionStore,
|
||||
auth::{AuthenticationMiddleware, AuthenticationProvider, User},
|
||||
};
|
||||
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(subscription_store))
|
||||
.service(RootResourceService::<PrincipalResource>::default().actix_resource())
|
||||
.service(RootResourceService::<PrincipalResource, User>::default().actix_resource())
|
||||
.service(
|
||||
web::scope("/principal").service(
|
||||
web::scope("/{principal}")
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use crate::addressbook::resource::AddressbookResource;
|
||||
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::auth::{AuthenticationProvider, User};
|
||||
use rustical_store::AddressbookStore;
|
||||
use rustical_store::auth::{AuthenticationProvider, User};
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
use std::sync::Arc;
|
||||
|
||||
@@ -74,6 +74,7 @@ impl Resource for PrincipalResource {
|
||||
type Prop = PrincipalPropWrapper;
|
||||
type Error = Error;
|
||||
type PrincipalResource = PrincipalResource;
|
||||
type Principal = User;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[
|
||||
@@ -140,6 +141,7 @@ impl<A: AddressbookStore, AP: AuthenticationProvider> ResourceService
|
||||
type MemberType = AddressbookResource;
|
||||
type Resource = PrincipalResource;
|
||||
type Error = Error;
|
||||
type Principal = User;
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -12,7 +12,6 @@ actix-web = { workspace = true }
|
||||
async-trait = { workspace = true }
|
||||
futures-util = { workspace = true }
|
||||
quick-xml = { workspace = true }
|
||||
rustical_store = { workspace = true }
|
||||
serde = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
itertools = { workspace = true }
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
use crate::{
|
||||
Principal,
|
||||
privileges::UserPrivilegeSet,
|
||||
resource::{NamedRoute, Resource},
|
||||
xml::{HrefElement, Resourcetype},
|
||||
};
|
||||
use actix_web::dev::ResourceMap;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
|
||||
@@ -31,7 +31,7 @@ pub trait CommonPropertiesExtension: Resource {
|
||||
fn get_prop(
|
||||
&self,
|
||||
rmap: &ResourceMap,
|
||||
user: &User,
|
||||
principal: &Self::Principal,
|
||||
prop: &CommonPropertiesPropName,
|
||||
) -> Result<CommonPropertiesProp, <Self as Resource>::Error> {
|
||||
Ok(match prop {
|
||||
@@ -40,13 +40,13 @@ pub trait CommonPropertiesExtension: Resource {
|
||||
}
|
||||
CommonPropertiesPropName::CurrentUserPrincipal => {
|
||||
CommonPropertiesProp::CurrentUserPrincipal(
|
||||
Self::PrincipalResource::get_url(rmap, [&user.id])
|
||||
Self::PrincipalResource::get_url(rmap, [&principal.get_id()])
|
||||
.unwrap()
|
||||
.into(),
|
||||
)
|
||||
}
|
||||
CommonPropertiesPropName::CurrentUserPrivilegeSet => {
|
||||
CommonPropertiesProp::CurrentUserPrivilegeSet(self.get_user_privileges(user)?)
|
||||
CommonPropertiesProp::CurrentUserPrivilegeSet(self.get_user_privileges(principal)?)
|
||||
}
|
||||
CommonPropertiesPropName::Owner => {
|
||||
CommonPropertiesProp::Owner(self.get_owner().map(|owner| {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
mod common;
|
||||
mod davpush;
|
||||
mod synctoken;
|
||||
|
||||
pub use common::*;
|
||||
pub use davpush::*;
|
||||
pub use synctoken::*;
|
||||
|
||||
@@ -3,9 +3,13 @@ pub mod error;
|
||||
pub mod extensions;
|
||||
pub mod namespace;
|
||||
pub mod privileges;
|
||||
pub mod push;
|
||||
pub mod resource;
|
||||
pub mod resources;
|
||||
pub mod xml;
|
||||
|
||||
use actix_web::FromRequest;
|
||||
pub use error::Error;
|
||||
|
||||
pub trait Principal: std::fmt::Debug + Clone + FromRequest + 'static {
|
||||
fn get_id(&self) -> &str;
|
||||
}
|
||||
|
||||
@@ -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::*;
|
||||
@@ -1,16 +1,15 @@
|
||||
use crate::Error;
|
||||
use crate::privileges::UserPrivilege;
|
||||
use crate::resource::Resource;
|
||||
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::IfNoneMatch;
|
||||
use actix_web::web;
|
||||
use actix_web::web::Data;
|
||||
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_actix_web::RootSpan;
|
||||
|
||||
@@ -18,7 +17,7 @@ use tracing_actix_web::RootSpan;
|
||||
pub async fn route_delete<R: ResourceService>(
|
||||
path: Path<R::PathComponents>,
|
||||
req: HttpRequest,
|
||||
user: User,
|
||||
principal: R::Principal,
|
||||
resource_service: Data<R>,
|
||||
root_span: RootSpan,
|
||||
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 privileges = resource.get_user_privileges(&user)?;
|
||||
let privileges = resource.get_user_privileges(&principal)?;
|
||||
if !privileges.has(&UserPrivilege::Write) {
|
||||
return Err(Error::Unauthorized.into());
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::Error;
|
||||
use crate::depth_header::Depth;
|
||||
use crate::privileges::UserPrivilege;
|
||||
use crate::resource::Resource;
|
||||
@@ -6,11 +7,9 @@ use crate::xml::MultistatusElement;
|
||||
use crate::xml::PropElement;
|
||||
use crate::xml::PropfindElement;
|
||||
use crate::xml::PropfindType;
|
||||
use crate::Error;
|
||||
use actix_web::HttpRequest;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::web::Path;
|
||||
use actix_web::HttpRequest;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_xml::XmlDocument;
|
||||
use tracing::instrument;
|
||||
use tracing_actix_web::RootSpan;
|
||||
@@ -21,7 +20,7 @@ pub(crate) async fn route_propfind<R: ResourceService>(
|
||||
path: Path<R::PathComponents>,
|
||||
body: String,
|
||||
req: HttpRequest,
|
||||
user: User,
|
||||
user: R::Principal,
|
||||
depth: Depth,
|
||||
root_span: RootSpan,
|
||||
resource_service: Data<R>,
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use crate::Error;
|
||||
use crate::privileges::UserPrivilege;
|
||||
use crate::resource::Resource;
|
||||
use crate::resource::ResourceService;
|
||||
use crate::xml::multistatus::{PropstatElement, PropstatWrapper, ResponseElement};
|
||||
use crate::xml::MultistatusElement;
|
||||
use crate::xml::TagList;
|
||||
use crate::Error;
|
||||
use crate::xml::multistatus::{PropstatElement, PropstatWrapper, ResponseElement};
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::web::Data;
|
||||
use actix_web::{web::Path, HttpRequest};
|
||||
use actix_web::{HttpRequest, web::Path};
|
||||
use quick_xml::name::Namespace;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_xml::EnumUnitVariants;
|
||||
use rustical_xml::Unparsed;
|
||||
use rustical_xml::XmlDeserialize;
|
||||
@@ -69,7 +68,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
||||
path: Path<R::PathComponents>,
|
||||
body: String,
|
||||
req: HttpRequest,
|
||||
user: User,
|
||||
principal: R::Principal,
|
||||
root_span: RootSpan,
|
||||
resource_service: Data<R>,
|
||||
) -> 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)?;
|
||||
|
||||
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) {
|
||||
return Err(Error::Unauthorized.into());
|
||||
}
|
||||
@@ -131,7 +130,7 @@ pub(crate) async fn route_proppatch<R: ResourceService>(
|
||||
}
|
||||
}
|
||||
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(
|
||||
&propname,
|
||||
) {
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
use crate::privileges::UserPrivilegeSet;
|
||||
use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper};
|
||||
use crate::xml::Resourcetype;
|
||||
use crate::xml::{multistatus::ResponseElement, TagList};
|
||||
use crate::Error;
|
||||
use crate::xml::multistatus::{PropTagWrapper, PropstatElement, PropstatWrapper};
|
||||
use crate::xml::{TagList, multistatus::ResponseElement};
|
||||
use crate::{Error, Principal};
|
||||
use actix_web::dev::ResourceMap;
|
||||
use actix_web::http::header::{EntityTag, IfMatch, IfNoneMatch};
|
||||
use actix_web::{http::StatusCode, ResponseError};
|
||||
use actix_web::{ResponseError, http::StatusCode};
|
||||
use itertools::Itertools;
|
||||
use quick_xml::name::Namespace;
|
||||
pub use resource_service::ResourceService;
|
||||
use rustical_store::auth::User;
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
use std::str::FromStr;
|
||||
|
||||
@@ -28,6 +27,7 @@ pub trait Resource: Clone + 'static {
|
||||
type Prop: ResourceProp + PartialEq + Clone + EnumVariants + EnumUnitVariants;
|
||||
type Error: ResponseError + From<crate::Error>;
|
||||
type PrincipalResource: Resource + NamedRoute;
|
||||
type Principal: Principal;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype;
|
||||
|
||||
@@ -38,7 +38,7 @@ pub trait Resource: Clone + 'static {
|
||||
fn get_prop(
|
||||
&self,
|
||||
rmap: &ResourceMap,
|
||||
user: &User,
|
||||
principal: &Self::Principal,
|
||||
prop: &<Self::Prop as EnumUnitVariants>::UnitVariants,
|
||||
) -> 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(
|
||||
&self,
|
||||
path: &str,
|
||||
props: &[&str],
|
||||
user: &User,
|
||||
principal: &Self::Principal,
|
||||
rmap: &ResourceMap,
|
||||
) -> Result<ResponseElement<Self::Prop>, Self::Error> {
|
||||
let mut props = props.to_vec();
|
||||
@@ -152,7 +155,7 @@ pub trait Resource: Clone + 'static {
|
||||
|
||||
let prop_responses = valid_props
|
||||
.into_iter()
|
||||
.map(|prop| self.get_prop(rmap, user, &prop))
|
||||
.map(|prop| self.get_prop(rmap, principal, &prop))
|
||||
.collect::<Result<Vec<_>, Self::Error>>()?;
|
||||
|
||||
let mut propstats = vec![PropstatWrapper::Normal(PropstatElement {
|
||||
|
||||
@@ -2,20 +2,23 @@ use actix_web::dev::{AppService, HttpServiceFactory};
|
||||
use actix_web::error::UrlGenerationError;
|
||||
use actix_web::test::TestRequest;
|
||||
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 serde::Deserialize;
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::methods::{route_delete, route_propfind, route_proppatch};
|
||||
use crate::Principal;
|
||||
|
||||
use super::Resource;
|
||||
use super::methods::{route_delete, route_propfind, route_proppatch};
|
||||
|
||||
#[async_trait(?Send)]
|
||||
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 Resource: Resource<Error = Self::Error>;
|
||||
type Resource: Resource<Error = Self::Error, Principal = Self::Principal>;
|
||||
type Error: ResponseError + From<crate::Error>;
|
||||
type Principal: Principal;
|
||||
|
||||
async fn get_members(
|
||||
&self,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use crate::Principal;
|
||||
use crate::extensions::{
|
||||
CommonPropertiesExtension, CommonPropertiesProp, CommonPropertiesPropName,
|
||||
};
|
||||
@@ -6,22 +7,22 @@ use crate::resource::{NamedRoute, Resource, ResourceService};
|
||||
use crate::xml::{Resourcetype, ResourcetypeInner};
|
||||
use actix_web::dev::ResourceMap;
|
||||
use async_trait::async_trait;
|
||||
use rustical_store::auth::User;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
#[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 {
|
||||
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 Error = PR::Error;
|
||||
type PrincipalResource = PR;
|
||||
type Principal = P;
|
||||
|
||||
fn get_resourcetype(&self) -> Resourcetype {
|
||||
Resourcetype(&[ResourcetypeInner(
|
||||
@@ -33,34 +34,37 @@ impl<PR: Resource + NamedRoute> Resource for RootResource<PR> {
|
||||
fn get_prop(
|
||||
&self,
|
||||
rmap: &ResourceMap,
|
||||
user: &User,
|
||||
user: &P,
|
||||
prop: &CommonPropertiesPropName,
|
||||
) -> Result<Self::Prop, Self::Error> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
#[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 {
|
||||
Self(PhantomData)
|
||||
Self(PhantomData, PhantomData)
|
||||
}
|
||||
}
|
||||
|
||||
#[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 MemberType = PR;
|
||||
type Resource = RootResource<PR>;
|
||||
type Resource = RootResource<PR, P>;
|
||||
type Error = PR::Error;
|
||||
type Principal = P;
|
||||
|
||||
async fn get_resource(&self, _: &()) -> Result<Self::Resource, Self::Error> {
|
||||
Ok(RootResource::<PR>::default())
|
||||
Ok(RootResource::<PR, P>::default())
|
||||
}
|
||||
}
|
||||
|
||||
25
crates/dav_push/Cargo.toml
Normal file
25
crates/dav_push/Cargo.toml
Normal 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
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::push::Transports;
|
||||
use crate::Transports;
|
||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||
|
||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumUnitVariants, EnumVariants)]
|
||||
@@ -6,9 +6,9 @@ use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize}
|
||||
pub enum DavPushExtensionProp {
|
||||
// WebDav Push
|
||||
#[xml(skip_deserializing)]
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
Transports(Transports),
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
Topic(String),
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ pub trait DavPushExtension {
|
||||
fn get_prop(
|
||||
&self,
|
||||
prop: &DavPushExtensionPropName,
|
||||
) -> Result<DavPushExtensionProp, crate::Error> {
|
||||
) -> Result<DavPushExtensionProp, rustical_dav::Error> {
|
||||
Ok(match &prop {
|
||||
DavPushExtensionPropName::Transports => {
|
||||
DavPushExtensionProp::Transports(Default::default())
|
||||
@@ -27,11 +27,11 @@ pub trait DavPushExtension {
|
||||
})
|
||||
}
|
||||
|
||||
fn set_prop(&self, _prop: DavPushExtensionProp) -> Result<(), crate::Error> {
|
||||
Err(crate::Error::PropReadOnly)
|
||||
fn set_prop(&self, _prop: DavPushExtensionProp) -> Result<(), rustical_dav::Error> {
|
||||
Err(rustical_dav::Error::PropReadOnly)
|
||||
}
|
||||
|
||||
fn remove_prop(&self, _prop: &DavPushExtensionPropName) -> Result<(), crate::Error> {
|
||||
Err(crate::Error::PropReadOnly)
|
||||
fn remove_prop(&self, _prop: &DavPushExtensionPropName) -> Result<(), rustical_dav::Error> {
|
||||
Err(rustical_dav::Error::PropReadOnly)
|
||||
}
|
||||
}
|
||||
7
crates/dav_push/src/lib.rs
Normal file
7
crates/dav_push/src/lib.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
mod extension;
|
||||
pub mod notifier;
|
||||
mod prop;
|
||||
pub mod register;
|
||||
|
||||
pub use extension::*;
|
||||
pub use prop::*;
|
||||
@@ -1,5 +1,5 @@
|
||||
use crate::xml::multistatus::PropstatElement;
|
||||
use actix_web::http::StatusCode;
|
||||
use rustical_dav::xml::multistatus::PropstatElement;
|
||||
use rustical_store::{CollectionOperation, CollectionOperationType, SubscriptionStore};
|
||||
use rustical_xml::{XmlRootTag, XmlSerialize, XmlSerializeRoot};
|
||||
use std::sync::Arc;
|
||||
@@ -8,17 +8,20 @@ use tracing::{error, info, warn};
|
||||
|
||||
#[derive(XmlSerialize, Debug)]
|
||||
struct PushMessageProp {
|
||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
topic: String,
|
||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
sync_token: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(XmlSerialize, XmlRootTag, Debug)]
|
||||
#[xml(root = b"push-message", ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns_prefix(crate::namespace::NS_DAVPUSH = b"", crate::namespace::NS_DAV = b"D",))]
|
||||
#[xml(root = b"push-message", ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns_prefix(
|
||||
rustical_dav::namespace::NS_DAVPUSH = b"",
|
||||
rustical_dav::namespace::NS_DAV = b"D",
|
||||
))]
|
||||
struct PushMessage {
|
||||
#[xml(ns = "crate::namespace::NS_DAV")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
|
||||
propstat: PropstatElement<PushMessageProp>,
|
||||
}
|
||||
|
||||
@@ -87,7 +90,10 @@ pub async fn push_notifier(
|
||||
error!("{err}");
|
||||
}
|
||||
} 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
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@ use rustical_xml::XmlSerialize;
|
||||
|
||||
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
|
||||
pub enum Transport {
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
WebPush,
|
||||
}
|
||||
|
||||
@@ -3,23 +3,23 @@ use rustical_xml::{XmlDeserialize, XmlRootTag};
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
pub struct WebPushSubscription {
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
pub push_resource: String,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, Clone, Debug, PartialEq)]
|
||||
pub struct SubscriptionElement {
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
pub web_push_subscription: WebPushSubscription,
|
||||
}
|
||||
|
||||
#[derive(XmlDeserialize, XmlRootTag, Clone, Debug, PartialEq)]
|
||||
#[xml(root = b"push-register")]
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
pub struct PushRegister {
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
pub subscription: SubscriptionElement,
|
||||
#[xml(ns = "crate::namespace::NS_DAVPUSH")]
|
||||
#[xml(ns = "rustical_dav::namespace::NS_DAVPUSH")]
|
||||
pub expires: Option<String>,
|
||||
}
|
||||
|
||||
@@ -10,9 +10,9 @@ use actix_web::{
|
||||
};
|
||||
use error::OidcError;
|
||||
use openidconnect::{
|
||||
AuthenticationFlow, AuthorizationCode, CsrfToken, EmptyAdditionalClaims, EndpointMaybeSet,
|
||||
EndpointNotSet, EndpointSet, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge,
|
||||
PkceCodeVerifier, RedirectUrl, TokenResponse, UserInfoClaims,
|
||||
AuthenticationFlow, AuthorizationCode, CsrfToken, EndpointMaybeSet, EndpointNotSet,
|
||||
EndpointSet, IssuerUrl, Nonce, OAuth2TokenResponse, PkceCodeChallenge, PkceCodeVerifier,
|
||||
RedirectUrl, TokenResponse, UserInfoClaims,
|
||||
core::{CoreClient, CoreGenderClaim, CoreProviderMetadata, CoreResponseType},
|
||||
};
|
||||
use rustical_store::auth::{AuthenticationProvider, User, user::PrincipalType::Individual};
|
||||
|
||||
@@ -30,6 +30,7 @@ tokio.workspace = true
|
||||
rand.workspace = true
|
||||
uuid.workspace = true
|
||||
clap.workspace = true
|
||||
rustical_dav.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
rstest = { workspace = true }
|
||||
|
||||
@@ -81,6 +81,12 @@ impl User {
|
||||
}
|
||||
}
|
||||
|
||||
impl rustical_dav::Principal for User {
|
||||
fn get_id(&self) -> &str {
|
||||
&self.id
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Display)]
|
||||
pub struct UnauthorizedError;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user