fix: URL-decode DAV paths with spaces + feat: app passwords for Basic Auth
Bug fix: - URL-decode paths in extract_webdav_path(), extract_caldav_path(), extract_carddav_path() so folders with spaces (e.g. 'My Folder') no longer return 404 when accessed via encoded URIs (%20) - Properly encode href values in PROPFIND/PROPPATCH/LOCK XML responses - Decode Destination header in MOVE/COPY operations New feature - App Passwords (API keys for DAV clients): - POST /api/auth/app-passwords → create (shows token once) - GET /api/auth/app-passwords → list (prefix only) - DELETE /api/auth/app-passwords/:id → revoke - Auth middleware now accepts both Bearer JWT and Basic Auth - Argon2 hashed, scoped (webdav/caldav/carddav), optional expiry - Compatible with DAVx5, Thunderbird, rclone, curl Tested: 12/12 E2E tests pass (create, list, WebDAV/CalDAV/CardDAV Basic Auth, URL-decode with spaces, wrong password 401, revoke, post- revoke 401).
This commit is contained in:
@@ -0,0 +1,85 @@
|
||||
//! DTOs for App Password (application-specific passwords for DAV clients).
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// ============================================================================
|
||||
// Request DTOs
|
||||
// ============================================================================
|
||||
|
||||
/// POST /api/auth/app-passwords — create a new app password
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct CreateAppPasswordRequestDto {
|
||||
/// Human-readable label (e.g. "DAVx5 on Pixel 8")
|
||||
pub label: String,
|
||||
/// Comma-separated scopes (defaults to all DAV protocols)
|
||||
#[serde(default = "default_scopes")]
|
||||
pub scopes: String,
|
||||
/// Optional expiration in days (None = never expires)
|
||||
pub expires_in_days: Option<u32>,
|
||||
}
|
||||
|
||||
fn default_scopes() -> String {
|
||||
"webdav,caldav,carddav".to_string()
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Response DTOs
|
||||
// ============================================================================
|
||||
|
||||
/// Response when an app password is created — includes the plain-text password
|
||||
/// that is shown ONCE to the user.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AppPasswordCreatedResponseDto {
|
||||
/// Unique identifier for this app password.
|
||||
pub id: String,
|
||||
/// The label chosen by the user.
|
||||
pub label: String,
|
||||
/// The plain-text app password — shown only ONCE.
|
||||
/// Format: `oxicloud-XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX`
|
||||
pub password: String,
|
||||
/// The username to use with HTTP Basic Auth.
|
||||
pub username: String,
|
||||
/// Active scopes.
|
||||
pub scopes: String,
|
||||
/// Expiration date or null for never.
|
||||
pub expires_at: Option<String>,
|
||||
/// Usage instructions for common clients.
|
||||
pub instructions: AppPasswordInstructions,
|
||||
}
|
||||
|
||||
/// Usage instructions included in the creation response.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AppPasswordInstructions {
|
||||
pub davx5: String,
|
||||
pub thunderbird: String,
|
||||
pub rclone: String,
|
||||
pub curl_example: String,
|
||||
}
|
||||
|
||||
/// Summary of an app password (list view — never includes the plain-text password).
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AppPasswordSummaryDto {
|
||||
pub id: String,
|
||||
pub label: String,
|
||||
/// First 8 chars of the token for identification.
|
||||
pub prefix: String,
|
||||
pub scopes: String,
|
||||
pub created_at: String,
|
||||
pub last_used_at: Option<String>,
|
||||
pub expires_at: Option<String>,
|
||||
pub active: bool,
|
||||
}
|
||||
|
||||
/// Response for list endpoint.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AppPasswordListResponseDto {
|
||||
pub app_passwords: Vec<AppPasswordSummaryDto>,
|
||||
pub total: usize,
|
||||
}
|
||||
|
||||
/// Response for revoke endpoint.
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct AppPasswordRevokeResponseDto {
|
||||
pub status: String,
|
||||
pub id: String,
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
pub mod address_book_dto;
|
||||
pub mod app_password_dto;
|
||||
pub mod calendar_dto;
|
||||
pub mod contact_dto;
|
||||
pub mod device_auth_dto;
|
||||
|
||||
Reference in New Issue
Block a user