From 6485b89c7321568cf6fc9e94b4f9fcc163724be2 Mon Sep 17 00:00:00 2001 From: Lennart <18233294+lennart-k@users.noreply.github.com> Date: Sun, 19 Jan 2025 00:20:16 +0100 Subject: [PATCH] Ensure all routes check for authorization --- crates/caldav/src/calendar/methods/post.rs | 17 ++++++++++++++++- crates/caldav/src/calendar/resource.rs | 2 +- crates/caldav/src/calendar_set/mod.rs | 2 +- crates/caldav/src/principal/mod.rs | 2 +- crates/carddav/src/address_object/methods.rs | 11 +++++++++-- crates/carddav/src/addressbook/resource.rs | 2 +- crates/dav/src/privileges.rs | 8 ++++++++ crates/frontend/src/routes/addressbook.rs | 14 +++++++++++--- crates/frontend/src/routes/calendar.rs | 14 +++++++++++--- 9 files changed, 59 insertions(+), 13 deletions(-) diff --git a/crates/caldav/src/calendar/methods/post.rs b/crates/caldav/src/calendar/methods/post.rs index 0574c9a..0828a57 100644 --- a/crates/caldav/src/calendar/methods/post.rs +++ b/crates/caldav/src/calendar/methods/post.rs @@ -1,8 +1,11 @@ +use crate::calendar::resource::CalendarResource; use crate::Error; 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_store::auth::User; use rustical_store::{CalendarStore, Subscription, SubscriptionStore}; use rustical_xml::XmlDocument; @@ -25,6 +28,18 @@ pub async fn route_post( } let calendar = store.get_calendar(&principal, &cal_id).await?; + let calendar_resource = CalendarResource { + cal: calendar, + read_only: true, + }; + + if !calendar_resource + .get_user_privileges(&user)? + .has(&UserPrivilege::Read) + { + return Err(Error::Unauthorized); + } + let request = PushRegister::parse_str(&body)?; let sub_id = uuid::Uuid::new_v4().to_string(); @@ -42,7 +57,7 @@ pub async fn route_post( .web_push_subscription .push_resource .to_owned(), - topic: calendar.push_topic, + topic: calendar_resource.cal.push_topic, expiration: expires.naive_local(), }; subscription_store.upsert_subscription(subscription).await?; diff --git a/crates/caldav/src/calendar/resource.rs b/crates/caldav/src/calendar/resource.rs index eba73aa..52d5133 100644 --- a/crates/caldav/src/calendar/resource.rs +++ b/crates/caldav/src/calendar/resource.rs @@ -283,7 +283,7 @@ impl Resource for CalendarResource { fn get_user_privileges(&self, user: &User) -> Result { if self.cal.subscription_url.is_some() || self.read_only { - return Ok(UserPrivilegeSet::read_only()); + return Ok(UserPrivilegeSet::owner_read(self.cal.principal == user.id)); } Ok(UserPrivilegeSet::owner_only(self.cal.principal == user.id)) diff --git a/crates/caldav/src/calendar_set/mod.rs b/crates/caldav/src/calendar_set/mod.rs index bbd1918..d987051 100644 --- a/crates/caldav/src/calendar_set/mod.rs +++ b/crates/caldav/src/calendar_set/mod.rs @@ -53,7 +53,7 @@ impl Resource for CalendarSetResource { } fn get_user_privileges(&self, user: &User) -> Result { - Ok(UserPrivilegeSet::owner_only(self.principal == user.id)) + Ok(UserPrivilegeSet::owner_read(self.principal == user.id)) } } diff --git a/crates/caldav/src/principal/mod.rs b/crates/caldav/src/principal/mod.rs index bb419b7..de6353a 100644 --- a/crates/caldav/src/principal/mod.rs +++ b/crates/caldav/src/principal/mod.rs @@ -113,7 +113,7 @@ impl Resource for PrincipalResource { } fn get_user_privileges(&self, user: &User) -> Result { - Ok(UserPrivilegeSet::owner_only(self.principal == user.id)) + Ok(UserPrivilegeSet::owner_read(self.principal == user.id)) } } diff --git a/crates/carddav/src/address_object/methods.rs b/crates/carddav/src/address_object/methods.rs index fe64edf..e7a4c8f 100644 --- a/crates/carddav/src/address_object/methods.rs +++ b/crates/carddav/src/address_object/methods.rs @@ -1,10 +1,13 @@ use super::resource::AddressObjectPathComponents; +use crate::addressbook::resource::AddressbookResource; use crate::Error; use actix_web::http::header; use actix_web::http::header::HeaderValue; use actix_web::web::{Data, Path}; use actix_web::HttpRequest; use actix_web::HttpResponse; +use rustical_dav::privileges::UserPrivilege; +use rustical_dav::resource::Resource; use rustical_store::auth::User; use rustical_store::{AddressObject, AddressbookStore}; use tracing::instrument; @@ -28,8 +31,12 @@ pub async fn get_object( } let addressbook = store.get_addressbook(&principal, &addressbook_id).await?; - if user.id != addressbook.principal { - return Ok(HttpResponse::Unauthorized().body("")); + let addressbook_resource = AddressbookResource(addressbook); + if !addressbook_resource + .get_user_privileges(&user)? + .has(&UserPrivilege::Read) + { + return Err(Error::Unauthorized); } let object = store diff --git a/crates/carddav/src/addressbook/resource.rs b/crates/carddav/src/addressbook/resource.rs index 54dc685..7cbd0cf 100644 --- a/crates/carddav/src/addressbook/resource.rs +++ b/crates/carddav/src/addressbook/resource.rs @@ -66,7 +66,7 @@ pub enum AddressbookPropWrapper { } #[derive(Clone, Debug, From, Into)] -pub struct AddressbookResource(Addressbook); +pub struct AddressbookResource(pub(crate) Addressbook); impl SyncTokenExtension for AddressbookResource { fn get_synctoken(&self) -> String { diff --git a/crates/dav/src/privileges.rs b/crates/dav/src/privileges.rs index 773f8f5..87c8eea 100644 --- a/crates/dav/src/privileges.rs +++ b/crates/dav/src/privileges.rs @@ -64,6 +64,14 @@ impl UserPrivilegeSet { } } + pub fn owner_read(is_owner: bool) -> Self { + if is_owner { + Self::read_only() + } else { + Self::default() + } + } + pub fn read_only() -> Self { Self { privileges: HashSet::from([ diff --git a/crates/frontend/src/routes/addressbook.rs b/crates/frontend/src/routes/addressbook.rs index 0f61923..e8d58a8 100644 --- a/crates/frontend/src/routes/addressbook.rs +++ b/crates/frontend/src/routes/addressbook.rs @@ -4,6 +4,7 @@ use actix_web::{ HttpRequest, HttpResponse, Responder, }; use askama::Template; +use askama_actix::TemplateToResponse; use rustical_store::{auth::User, Addressbook, AddressbookStore}; #[derive(Template)] @@ -15,21 +16,28 @@ struct AddressbookPage { pub async fn route_addressbook( path: Path<(String, String)>, store: Data, - _user: User, + user: User, ) -> Result { let (owner, addrbook_id) = path.into_inner(); + if owner != user.id { + return Ok(HttpResponse::Unauthorized().body("Unauthorized")); + } Ok(AddressbookPage { addressbook: store.get_addressbook(&owner, &addrbook_id).await?, - }) + } + .to_response()) } pub async fn route_addressbook_restore( path: Path<(String, String)>, req: HttpRequest, store: Data, - _user: User, + user: User, ) -> Result { let (owner, addressbook_id) = path.into_inner(); + if owner != user.id { + return Ok(HttpResponse::Unauthorized().body("Unauthorized")); + } store.restore_addressbook(&owner, &addressbook_id).await?; Ok(match req.headers().get(header::REFERER) { Some(referer) => web::Redirect::to(referer.to_str().unwrap().to_owned()) diff --git a/crates/frontend/src/routes/calendar.rs b/crates/frontend/src/routes/calendar.rs index 931b5e4..a264217 100644 --- a/crates/frontend/src/routes/calendar.rs +++ b/crates/frontend/src/routes/calendar.rs @@ -4,6 +4,7 @@ use actix_web::{ HttpRequest, HttpResponse, Responder, }; use askama::Template; +use askama_actix::TemplateToResponse; use rustical_store::{auth::User, Calendar, CalendarStore}; #[derive(Template)] @@ -15,21 +16,28 @@ struct CalendarPage { pub async fn route_calendar( path: Path<(String, String)>, store: Data, - _user: User, + user: User, ) -> Result { let (owner, cal_id) = path.into_inner(); + if owner != user.id { + return Ok(HttpResponse::Unauthorized().body("Unauthorized")); + } Ok(CalendarPage { calendar: store.get_calendar(&owner, &cal_id).await?, - }) + } + .to_response()) } pub async fn route_calendar_restore( path: Path<(String, String)>, req: HttpRequest, store: Data, - _user: User, + user: User, ) -> Result { let (owner, cal_id) = path.into_inner(); + if owner != user.id { + return Ok(HttpResponse::Unauthorized().body("Unauthorized")); + } store.restore_calendar(&owner, &cal_id).await?; Ok(match req.headers().get(header::REFERER) { Some(referer) => web::Redirect::to(referer.to_str().unwrap().to_owned())