mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 18:12:27 +00:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d59ae25eba | ||
|
|
d4daa35df6 | ||
|
|
ea43876410 | ||
|
|
18af1b9aa2 | ||
|
|
e69c75102c | ||
|
|
09f1bd20ae | ||
|
|
72f970a857 | ||
|
|
08c250657e |
22
Cargo.lock
generated
22
Cargo.lock
generated
@@ -2999,7 +2999,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"argon2",
|
||||
@@ -3042,7 +3042,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_caldav"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"async-std",
|
||||
"async-trait",
|
||||
@@ -3080,7 +3080,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_carddav"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3112,7 +3112,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_dav"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3137,7 +3137,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_dav_push"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3163,7 +3163,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_frontend"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"askama",
|
||||
"askama_web",
|
||||
@@ -3196,7 +3196,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_ical"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"axum",
|
||||
"chrono",
|
||||
@@ -3214,7 +3214,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_oidc"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -3229,7 +3229,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_store"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"async-trait",
|
||||
@@ -3263,7 +3263,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_store_sqlite"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"chrono",
|
||||
@@ -3284,7 +3284,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustical_xml"
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
dependencies = [
|
||||
"quick-xml",
|
||||
"thiserror 2.0.12",
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
members = ["crates/*"]
|
||||
|
||||
[workspace.package]
|
||||
version = "0.6.2"
|
||||
version = "0.6.5"
|
||||
edition = "2024"
|
||||
description = "A CalDAV server"
|
||||
repository = "https://github.com/lennart-k/rustical"
|
||||
|
||||
@@ -51,7 +51,7 @@ impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourc
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access, calendar-proxy, webdav-push";
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access, webdav-push";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -41,11 +41,6 @@ impl Resource for PrincipalResource {
|
||||
Resourcetype(&[
|
||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "collection"),
|
||||
ResourcetypeInner(Some(rustical_dav::namespace::NS_DAV), "principal"),
|
||||
// https://github.com/apple/ccs-calendarserver/blob/13c706b985fb728b9aab42dc0fef85aae21921c3/doc/Extensions/caldav-proxy.txt
|
||||
// ResourcetypeInner(
|
||||
// Some(rustical_dav::namespace::NS_CALENDARSERVER),
|
||||
// "calendar-proxy-write",
|
||||
// ),
|
||||
])
|
||||
}
|
||||
|
||||
|
||||
@@ -46,7 +46,7 @@ impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore> Resour
|
||||
type Principal = Principal;
|
||||
type PrincipalUri = CalDavPrincipalUri;
|
||||
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access, calendar-proxy";
|
||||
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
|
||||
|
||||
async fn get_resource(
|
||||
&self,
|
||||
|
||||
@@ -56,9 +56,13 @@ pub async fn route_post_app_token<AP: AuthenticationProvider>(
|
||||
assert!(!name.is_empty());
|
||||
assert_eq!(user_id, user.id);
|
||||
let token = generate_app_token();
|
||||
auth_provider
|
||||
let mut token_id = auth_provider
|
||||
.add_app_token(&user.id, name.to_owned(), token.clone())
|
||||
.await?;
|
||||
// Get first 4 characters of token identifier
|
||||
token_id.truncate(4);
|
||||
// This will be a hint for the token validator which app token hash to verify against
|
||||
let token = format!("{token_id}_{token}");
|
||||
if apple {
|
||||
let profile = AppleConfig {
|
||||
token_name: name,
|
||||
|
||||
@@ -149,8 +149,23 @@ impl AuthenticationProvider for SqlitePrincipalStore {
|
||||
user_id: &str,
|
||||
token: &str,
|
||||
) -> Result<Option<Principal>, Error> {
|
||||
#[instrument(skip(password))]
|
||||
fn verify_password(password: &str, hash: &str) -> Result<(), password_auth::VerifyError> {
|
||||
password_auth::verify_password(password, hash)
|
||||
}
|
||||
|
||||
// Allow to specify the token id to use to make validation faster
|
||||
// Doesn't match the whole length of the token id to keep the length in bounds
|
||||
// Example: asd_selgkh
|
||||
// where the app token id starts with asd and its value is selgkh
|
||||
let (token_id_prefix, token) = token.split_once('_').unwrap_or(("", token));
|
||||
|
||||
for app_token in &self.get_app_tokens(user_id).await? {
|
||||
if password_auth::verify_password(token, app_token.token.as_ref()).is_ok() {
|
||||
// Wrong token id
|
||||
if !app_token.id.starts_with(token_id_prefix) {
|
||||
continue;
|
||||
}
|
||||
if verify_password(token, app_token.token.as_ref()).is_ok() {
|
||||
return self.get_principal(user_id).await;
|
||||
}
|
||||
}
|
||||
@@ -206,7 +221,10 @@ impl AuthenticationProvider for SqlitePrincipalStore {
|
||||
None,
|
||||
None,
|
||||
Params {
|
||||
rounds: 10,
|
||||
// The app token has a high entropy so we are quite safe from quessing attacks
|
||||
// Also if an attacker got access to the hashes they'd have already gotten
|
||||
// access to the whole database.
|
||||
rounds: 2,
|
||||
..Default::default()
|
||||
},
|
||||
&salt,
|
||||
|
||||
23
src/app.rs
23
src/app.rs
@@ -1,11 +1,13 @@
|
||||
use crate::config::NextcloudLoginConfig;
|
||||
use axum::Router;
|
||||
use axum::body::Body;
|
||||
use axum::body::{Body, HttpBody};
|
||||
use axum::extract::Request;
|
||||
use axum::middleware::Next;
|
||||
use axum::response::{Redirect, Response};
|
||||
use axum::routing::{any, options};
|
||||
use axum_extra::TypedHeader;
|
||||
use headers::{HeaderMapExt, UserAgent};
|
||||
use http::header::CONNECTION;
|
||||
use http::{HeaderValue, StatusCode};
|
||||
use rustical_caldav::caldav_router;
|
||||
use rustical_carddav::carddav_router;
|
||||
@@ -60,7 +62,7 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
|
||||
.route(
|
||||
"/.well-known/caldav",
|
||||
any(async |TypedHeader(ua): TypedHeader<UserAgent>| {
|
||||
if ua.as_str().contains("remindd") {
|
||||
if ua.as_str().contains("remindd") || ua.as_str().contains("dataaccessd") {
|
||||
// remindd is an Apple Calendar User Agent
|
||||
// Even when explicitly configuring a principal URL in Apple Calendar Apple
|
||||
// will not respect that configuration but call /.well-known/caldav,
|
||||
@@ -178,4 +180,21 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
|
||||
},
|
||||
),
|
||||
)
|
||||
.layer(axum::middleware::from_fn(
|
||||
async |req: Request, next: Next| {
|
||||
// Closes the connection if the request body might've not been fully consumed
|
||||
// Otherwise subsequent requests reusing the connection might fail.
|
||||
// See https://github.com/lennart-k/rustical/issues/77
|
||||
let body_empty = req.body().is_end_stream();
|
||||
let mut response = next.run(req).await;
|
||||
if !body_empty
|
||||
&& (response.status().is_server_error() || response.status().is_client_error())
|
||||
{
|
||||
response
|
||||
.headers_mut()
|
||||
.insert(CONNECTION, HeaderValue::from_static("close"));
|
||||
}
|
||||
response
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user