Switch to new auth backend

This commit is contained in:
Lennart
2023-09-07 19:03:28 +02:00
parent 855cd4dab6
commit bdbb47422e
5 changed files with 46 additions and 56 deletions

View File

@@ -1,9 +1,9 @@
use actix_web::http::Method; use actix_web::http::Method;
use actix_web::web::{self, Data}; use actix_web::web::{self, Data};
use actix_web::{guard, HttpResponse, Responder}; use actix_web::{guard, HttpResponse, Responder};
use actix_web_httpauth::middleware::HttpAuthentication;
use error::Error; use error::Error;
use routes::{calendar, event, principal, root}; use routes::{calendar, event, principal, root};
use rustical_auth::CheckAuthentication;
use rustical_store::calendar::CalendarStore; use rustical_store::calendar::CalendarStore;
use std::str::FromStr; use std::str::FromStr;
use std::sync::Arc; use std::sync::Arc;
@@ -24,7 +24,6 @@ pub fn configure_well_known(cfg: &mut web::ServiceConfig, caldav_root: String) {
cfg.service(web::redirect("/caldav", caldav_root).permanent()); cfg.service(web::redirect("/caldav", caldav_root).permanent());
} }
pub fn configure_dav<C: CalendarStore>(
pub fn configure_dav<A: CheckAuthentication, C: CalendarStore>( pub fn configure_dav<A: CheckAuthentication, C: CalendarStore>(
cfg: &mut web::ServiceConfig, cfg: &mut web::ServiceConfig,
prefix: String, prefix: String,
@@ -34,16 +33,9 @@ pub fn configure_dav<A: CheckAuthentication, C: CalendarStore>(
let propfind_method = || Method::from_str("PROPFIND").unwrap(); let propfind_method = || Method::from_str("PROPFIND").unwrap();
let report_method = || Method::from_str("REPORT").unwrap(); let report_method = || Method::from_str("REPORT").unwrap();
let auth = HttpAuthentication::basic(|req, creds| async move {
if creds.user_id().is_empty() {
// not authenticated
Err((actix_web::error::ErrorUnauthorized("Unauthorized"), req))
} else {
Ok(req)
}
});
cfg.app_data(Data::new(CalDavContext { prefix, store })) cfg.app_data(Data::new(CalDavContext { prefix, store }))
.app_data(Data::from(auth))
.service( .service(
web::resource("{path:.*}") web::resource("{path:.*}")
// Without the guard this service would handle all requests // Without the guard this service would handle all requests
@@ -52,26 +44,24 @@ pub fn configure_dav<A: CheckAuthentication, C: CalendarStore>(
) )
.service( .service(
web::resource("") web::resource("")
.route(web::method(propfind_method()).to(root::route_propfind_root::<C>)) .route(web::method(propfind_method()).to(root::route_propfind_root::<A, C>)),
.wrap(auth.clone()),
) )
.service( .service(
web::resource("/{principal}") web::resource("/{principal}").route(
.route(web::method(propfind_method()).to(principal::route_propfind_principal::<C>)) web::method(propfind_method()).to(principal::route_propfind_principal::<A, C>),
.wrap(auth.clone()), ),
) )
.service( .service(
web::resource("/{principal}/{calendar}") web::resource("/{principal}/{calendar}")
.route(web::method(report_method()).to(calendar::route_report_calendar::<C>)) .route(web::method(report_method()).to(calendar::route_report_calendar::<A, C>))
.route(web::method(propfind_method()).to(calendar::route_propfind_calendar::<C>)) .route(web::method(propfind_method()).to(calendar::route_propfind_calendar::<A, C>))
.wrap(auth.clone()), .route(web::method(mkcol_method()).to(calendar::route_mkcol_calendar::<A, C>)),
) )
.service( .service(
web::resource("/{principal}/{calendar}/{event}") web::resource("/{principal}/{calendar}/{event}")
.route(web::method(Method::DELETE).to(event::delete_event::<C>)) .route(web::method(Method::DELETE).to(event::delete_event::<A, C>))
.route(web::method(Method::GET).to(event::get_event::<C>)) .route(web::method(Method::GET).to(event::get_event::<A, C>))
.route(web::method(Method::PUT).to(event::put_event::<C>)) .route(web::method(Method::PUT).to(event::put_event::<A, C>)),
.wrap(auth.clone()),
); );
} }

View File

@@ -1,3 +1,4 @@
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
use crate::namespace::Namespace; use crate::namespace::Namespace;
use crate::propfind::{ use crate::propfind::{
generate_multistatus, parse_propfind, write_invalid_props_response, write_propstat_response, generate_multistatus, parse_propfind, write_invalid_props_response, write_propstat_response,
@@ -8,7 +9,6 @@ use actix_web::http::header::ContentType;
use actix_web::http::StatusCode; use actix_web::http::StatusCode;
use actix_web::web::{Data, Path}; use actix_web::web::{Data, Path};
use actix_web::{HttpRequest, HttpResponse}; use actix_web::{HttpRequest, HttpResponse};
use actix_web_httpauth::extractors::basic::BasicAuth;
use anyhow::Result; use anyhow::Result;
use quick_xml::events::BytesText; use quick_xml::events::BytesText;
use quick_xml::Writer; use quick_xml::Writer;
@@ -69,12 +69,14 @@ async fn handle_report_calendar_query(
.body(output)) .body(output))
} }
pub async fn route_report_calendar<C: CalendarStore>( pub async fn route_report_calendar<A: CheckAuthentication, C: CalendarStore>(
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
body: String, body: String,
path: Path<(String, String)>, path: Path<(String, String)>,
request: HttpRequest, request: HttpRequest,
_auth: AuthInfoExtractor<A>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
// TODO: Check authorization
let (_principal, cid) = path.into_inner(); let (_principal, cid) = path.into_inner();
let doc = roxmltree::Document::parse(&body).map_err(|_e| Error::InternalError)?; let doc = roxmltree::Document::parse(&body).map_err(|_e| Error::InternalError)?;
@@ -167,13 +169,14 @@ pub fn generate_propfind_calendar_response(
Ok(std::str::from_utf8(&output_buffer)?.to_string()) Ok(std::str::from_utf8(&output_buffer)?.to_string())
} }
pub async fn route_propfind_calendar<C: CalendarStore>( pub async fn route_propfind_calendar<A: CheckAuthentication, C: CalendarStore>(
path: Path<(String, String)>, path: Path<(String, String)>,
body: String, body: String,
request: HttpRequest, request: HttpRequest,
auth: BasicAuth,
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
auth: AuthInfoExtractor<A>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
// TODO: Check authorization
let (_principal, cid) = path.into_inner(); let (_principal, cid) = path.into_inner();
let calendar = context let calendar = context
.store .store
@@ -187,7 +190,7 @@ pub async fn route_propfind_calendar<C: CalendarStore>(
let responses_string = generate_propfind_calendar_response( let responses_string = generate_propfind_calendar_response(
props.clone(), props.clone(),
auth.user_id(), &auth.inner.user_id,
request.path(), request.path(),
&context.prefix, &context.prefix,
&calendar, &calendar,

View File

@@ -1,12 +1,16 @@
use crate::{CalDavContext, Error}; use crate::{CalDavContext, Error};
use actix_web::web::{Data, Path}; use actix_web::web::{Data, Path};
use actix_web::HttpResponse; use actix_web::HttpResponse;
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
use rustical_store::calendar::CalendarStore; use rustical_store::calendar::CalendarStore;
pub async fn delete_event<C: CalendarStore>( pub async fn delete_event<A: CheckAuthentication, C: CalendarStore>(
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
path: Path<(String, String, String)>, path: Path<(String, String, String)>,
auth: AuthInfoExtractor<A>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let _user = auth.inner.user_id;
// TODO: verify whether user is authorized
let (_principal, mut cid, uid) = path.into_inner(); let (_principal, mut cid, uid) = path.into_inner();
if cid.ends_with(".ics") { if cid.ends_with(".ics") {
cid.truncate(cid.len() - 4); cid.truncate(cid.len() - 4);
@@ -22,13 +26,12 @@ pub async fn delete_event<C: CalendarStore>(
Ok(HttpResponse::Ok().body("")) Ok(HttpResponse::Ok().body(""))
} }
pub async fn get_event<C: CalendarStore>( pub async fn get_event<A: CheckAuthentication, C: CalendarStore>(
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
path: Path<(String, String, String)>, path: Path<(String, String, String)>,
_auth: AuthInfoExtractor<A>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let (_principal, mut cid, uid) = path.into_inner(); // TODO: verify whether user is authorized
if cid.ends_with(".ics") {
cid.truncate(cid.len() - 4);
} }
let event = context let event = context
.store .store
@@ -43,10 +46,11 @@ pub async fn get_event<C: CalendarStore>(
.body(event.to_ics().to_string())) .body(event.to_ics().to_string()))
} }
pub async fn put_event<C: CalendarStore>( pub async fn put_event<A: CheckAuthentication, C: CalendarStore>(
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
path: Path<(String, String, String)>, path: Path<(String, String, String)>,
body: String, body: String,
_auth: AuthInfoExtractor<A>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let (_principal, mut cid, uid) = path.into_inner(); let (_principal, mut cid, uid) = path.into_inner();
// Incredibly bodged method of normalising the uid but works for a prototype // Incredibly bodged method of normalising the uid but works for a prototype

View File

@@ -13,12 +13,12 @@ use actix_web::{
web::Data, web::Data,
HttpRequest, HttpResponse, HttpRequest, HttpResponse,
}; };
use actix_web_httpauth::extractors::basic::BasicAuth;
use anyhow::Result; use anyhow::Result;
use quick_xml::{ use quick_xml::{
events::{BytesText, Event}, events::{BytesText, Event},
Writer, Writer,
}; };
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
use rustical_store::calendar::CalendarStore; use rustical_store::calendar::CalendarStore;
// Executes the PROPFIND request and returns a XML string to be written into a <mulstistatus> object. // Executes the PROPFIND request and returns a XML string to be written into a <mulstistatus> object.
@@ -86,13 +86,14 @@ pub async fn generate_propfind_principal_response(
Ok(std::str::from_utf8(&output_buffer)?.to_string()) Ok(std::str::from_utf8(&output_buffer)?.to_string())
} }
pub async fn route_propfind_principal<C: CalendarStore>( pub async fn route_propfind_principal<A: CheckAuthentication, C: CalendarStore>(
body: String, body: String,
request: HttpRequest, request: HttpRequest,
auth: BasicAuth, auth: AuthInfoExtractor<A>,
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
depth: Depth, depth: Depth,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let user = &auth.inner.user_id;
let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?; let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?;
let mut responses = Vec::new(); let mut responses = Vec::new();
@@ -110,7 +111,7 @@ pub async fn route_propfind_principal<C: CalendarStore>(
responses.push( responses.push(
generate_propfind_calendar_response( generate_propfind_calendar_response(
props.clone(), props.clone(),
auth.user_id(), &auth.inner.user_id,
&format!("{}/{}", request.path(), cal.id), &format!("{}/{}", request.path(), cal.id),
&context.prefix, &context.prefix,
&cal, &cal,
@@ -121,14 +122,9 @@ pub async fn route_propfind_principal<C: CalendarStore>(
} }
responses.push( responses.push(
generate_propfind_principal_response( generate_propfind_principal_response(props.clone(), user, request.path(), &context.prefix)
props.clone(), .await
auth.user_id(), .map_err(|_e| Error::InternalError)?,
request.path(),
&context.prefix,
)
.await
.map_err(|_e| Error::InternalError)?,
); );
let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| { let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| {

View File

@@ -3,11 +3,11 @@ use actix_web::{
web::Data, web::Data,
HttpRequest, HttpResponse, HttpRequest, HttpResponse,
}; };
use actix_web_httpauth::extractors::basic::BasicAuth;
use quick_xml::{ use quick_xml::{
events::{BytesText, Event}, events::{BytesText, Event},
Writer, Writer,
}; };
use rustical_auth::{AuthInfoExtractor, CheckAuthentication};
use rustical_store::calendar::CalendarStore; use rustical_store::calendar::CalendarStore;
use crate::{ use crate::{
@@ -64,22 +64,19 @@ pub async fn generate_propfind_root_response(
Ok(std::str::from_utf8(&output_buffer)?.to_string()) Ok(std::str::from_utf8(&output_buffer)?.to_string())
} }
pub async fn route_propfind_root<C: CalendarStore>( pub async fn route_propfind_root<A: CheckAuthentication, C: CalendarStore>(
body: String, body: String,
request: HttpRequest, request: HttpRequest,
auth: BasicAuth,
context: Data<CalDavContext<C>>, context: Data<CalDavContext<C>>,
auth: AuthInfoExtractor<A>,
) -> Result<HttpResponse, Error> { ) -> Result<HttpResponse, Error> {
let principal = &auth.inner.user_id;
let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?; let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?;
let responses_string = generate_propfind_root_response( let responses_string =
props.clone(), generate_propfind_root_response(props.clone(), principal, request.path(), &context.prefix)
auth.user_id(), .await
request.path(), .map_err(|_e| Error::InternalError)?;
&context.prefix,
)
.await
.map_err(|_e| Error::InternalError)?;
let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| { let output = generate_multistatus(vec![Namespace::Dav, Namespace::CalDAV], |writer| {
writer.write_event(Event::Text(BytesText::from_escaped(responses_string)))?; writer.write_event(Event::Text(BytesText::from_escaped(responses_string)))?;