Clean up obsolete CalDAV environment variables
Some checks failed
Build and Push Docker Image / docker (push) Failing after 2s
Some checks failed
Build and Push Docker Image / docker (push) Failing after 2s
## Removed Obsolete Environment Variables: - `CALDAV_SERVER_URL` - provided by user login - `CALDAV_USERNAME` - provided by user login - `CALDAV_PASSWORD` - provided by user login - `CALDAV_TASKS_PATH` - not used in any features ## Kept with Intelligent Discovery: - `CALDAV_CALENDAR_PATH` - optional override, defaults to smart discovery ## Changes: ### Backend - Remove `CalDAVConfig::from_env()` method (not used in main app) - Add `CalDAVConfig::new()` constructor with credentials - Remove `tasks_path` field from CalDAVConfig - Update auth service to use new constructor - Update tests to use hardcoded test values instead of env vars - Update debug tools to use test credentials ### Frontend - Remove unused `config.rs` file entirely (frontend uses backend API) ## Current Authentication Flow: 1. User provides CalDAV credentials via login API 2. Backend creates CalDAVConfig dynamically from login request 3. Backend tests authentication via calendar discovery 4. Optional `CALDAV_CALENDAR_PATH` env var can override discovery 5. No environment variables required for normal operation This simplifies deployment - users only need to provide CalDAV credentials through the web interface, no server-side configuration required. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,284 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::env;
|
||||
use base64::prelude::*;
|
||||
|
||||
/// Configuration for CalDAV server connection and authentication.
|
||||
///
|
||||
/// This struct holds all the necessary information to connect to a CalDAV server,
|
||||
/// including server URL, credentials, and optional collection paths.
|
||||
///
|
||||
/// # Security Note
|
||||
///
|
||||
/// The password field contains sensitive information and should be handled carefully.
|
||||
/// This struct implements `Debug` but in production, consider implementing a custom
|
||||
/// `Debug` that masks the password field.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use crate::config::CalDAVConfig;
|
||||
///
|
||||
/// // Load configuration from environment variables
|
||||
/// let config = CalDAVConfig::from_env()?;
|
||||
///
|
||||
/// // Use the configuration for HTTP requests
|
||||
/// let auth_header = format!("Basic {}", config.get_basic_auth());
|
||||
/// ```
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CalDAVConfig {
|
||||
/// The base URL of the CalDAV server (e.g., "https://caldav.example.com/dav/")
|
||||
pub server_url: String,
|
||||
|
||||
/// Username for authentication with the CalDAV server
|
||||
pub username: String,
|
||||
|
||||
/// Password for authentication with the CalDAV server
|
||||
///
|
||||
/// **Security Note**: This contains sensitive information
|
||||
pub password: String,
|
||||
|
||||
/// Optional path to the calendar collection on the server
|
||||
///
|
||||
/// If not provided, the client will need to discover available calendars
|
||||
/// through CalDAV PROPFIND requests
|
||||
pub calendar_path: Option<String>,
|
||||
|
||||
/// Optional path to the tasks/todo collection on the server
|
||||
///
|
||||
/// Some CalDAV servers store tasks separately from calendar events
|
||||
pub tasks_path: Option<String>,
|
||||
}
|
||||
|
||||
impl CalDAVConfig {
|
||||
/// Creates a new CalDAVConfig by loading values from environment variables.
|
||||
///
|
||||
/// This method will attempt to load a `.env` file from the current directory
|
||||
/// and then read the following required environment variables:
|
||||
///
|
||||
/// - `CALDAV_SERVER_URL`: The CalDAV server base URL
|
||||
/// - `CALDAV_USERNAME`: Username for authentication
|
||||
/// - `CALDAV_PASSWORD`: Password for authentication
|
||||
///
|
||||
/// Optional environment variables:
|
||||
///
|
||||
/// - `CALDAV_CALENDAR_PATH`: Path to calendar collection
|
||||
/// - `CALDAV_TASKS_PATH`: Path to tasks collection
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `ConfigError::MissingVar` if any required environment variable
|
||||
/// is not set or cannot be read.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use crate::config::CalDAVConfig;
|
||||
///
|
||||
/// match CalDAVConfig::from_env() {
|
||||
/// Ok(config) => {
|
||||
/// println!("Loaded config for server: {}", config.server_url);
|
||||
/// }
|
||||
/// Err(e) => {
|
||||
/// eprintln!("Failed to load config: {}", e);
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub fn from_env() -> Result<Self, ConfigError> {
|
||||
// Attempt to load .env file, but don't fail if it doesn't exist
|
||||
dotenvy::dotenv().ok();
|
||||
|
||||
let server_url = env::var("CALDAV_SERVER_URL")
|
||||
.map_err(|_| ConfigError::MissingVar("CALDAV_SERVER_URL".to_string()))?;
|
||||
|
||||
let username = env::var("CALDAV_USERNAME")
|
||||
.map_err(|_| ConfigError::MissingVar("CALDAV_USERNAME".to_string()))?;
|
||||
|
||||
let password = env::var("CALDAV_PASSWORD")
|
||||
.map_err(|_| ConfigError::MissingVar("CALDAV_PASSWORD".to_string()))?;
|
||||
|
||||
// Optional paths - it's fine if these are not set
|
||||
let calendar_path = env::var("CALDAV_CALENDAR_PATH").ok();
|
||||
let tasks_path = env::var("CALDAV_TASKS_PATH").ok();
|
||||
|
||||
Ok(CalDAVConfig {
|
||||
server_url,
|
||||
username,
|
||||
password,
|
||||
calendar_path,
|
||||
tasks_path,
|
||||
})
|
||||
}
|
||||
|
||||
/// Generates a Base64-encoded string for HTTP Basic Authentication.
|
||||
///
|
||||
/// This method combines the username and password in the format
|
||||
/// `username:password` and encodes it using Base64, which is the
|
||||
/// standard format for the `Authorization: Basic` HTTP header.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// A Base64-encoded string that can be used directly in the
|
||||
/// `Authorization` header: `Authorization: Basic <returned_value>`
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use crate::config::CalDAVConfig;
|
||||
///
|
||||
/// let config = CalDAVConfig {
|
||||
/// server_url: "https://example.com".to_string(),
|
||||
/// username: "user".to_string(),
|
||||
/// password: "pass".to_string(),
|
||||
/// calendar_path: None,
|
||||
/// tasks_path: None,
|
||||
/// };
|
||||
///
|
||||
/// let auth_value = config.get_basic_auth();
|
||||
/// let auth_header = format!("Basic {}", auth_value);
|
||||
/// ```
|
||||
pub fn get_basic_auth(&self) -> String {
|
||||
let credentials = format!("{}:{}", self.username, self.password);
|
||||
BASE64_STANDARD.encode(&credentials)
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors that can occur when loading or using CalDAV configuration.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum ConfigError {
|
||||
/// A required environment variable is missing or cannot be read.
|
||||
///
|
||||
/// This error occurs when calling `CalDAVConfig::from_env()` and one of the
|
||||
/// required environment variables (`CALDAV_SERVER_URL`, `CALDAV_USERNAME`,
|
||||
/// or `CALDAV_PASSWORD`) is not set.
|
||||
#[error("Missing environment variable: {0}")]
|
||||
MissingVar(String),
|
||||
|
||||
/// The configuration contains invalid or malformed values.
|
||||
///
|
||||
/// This could include malformed URLs, invalid authentication credentials,
|
||||
/// or other configuration issues that prevent proper CalDAV operation.
|
||||
#[error("Invalid configuration: {0}")]
|
||||
Invalid(String),
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_basic_auth_encoding() {
|
||||
let config = CalDAVConfig {
|
||||
server_url: "https://example.com".to_string(),
|
||||
username: "testuser".to_string(),
|
||||
password: "testpass".to_string(),
|
||||
calendar_path: None,
|
||||
tasks_path: None,
|
||||
};
|
||||
|
||||
let auth = config.get_basic_auth();
|
||||
let expected = BASE64_STANDARD.encode("testuser:testpass");
|
||||
assert_eq!(auth, expected);
|
||||
}
|
||||
|
||||
/// Integration test that authenticates with the actual Baikal CalDAV server
|
||||
///
|
||||
/// This test requires a valid .env file with:
|
||||
/// - CALDAV_SERVER_URL
|
||||
/// - CALDAV_USERNAME
|
||||
/// - CALDAV_PASSWORD
|
||||
///
|
||||
/// Run with: `cargo test test_baikal_auth`
|
||||
#[tokio::test]
|
||||
async fn test_baikal_auth() {
|
||||
// Load config from .env
|
||||
let config = CalDAVConfig::from_env()
|
||||
.expect("Failed to load CalDAV config from environment");
|
||||
|
||||
println!("Testing authentication to: {}", config.server_url);
|
||||
|
||||
// Create HTTP client
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// Make a simple OPTIONS request to test authentication
|
||||
let response = client
|
||||
.request(reqwest::Method::OPTIONS, &config.server_url)
|
||||
.header("Authorization", format!("Basic {}", config.get_basic_auth()))
|
||||
.header("User-Agent", "calendar-app/0.1.0")
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send request to CalDAV server");
|
||||
|
||||
println!("Response status: {}", response.status());
|
||||
println!("Response headers: {:#?}", response.headers());
|
||||
|
||||
// Check if we got a successful response or at least not a 401 Unauthorized
|
||||
assert!(
|
||||
response.status().is_success() || response.status() != 401,
|
||||
"Authentication failed with status: {}. Check your credentials in .env",
|
||||
response.status()
|
||||
);
|
||||
|
||||
// For Baikal/CalDAV servers, we should see DAV headers
|
||||
assert!(
|
||||
response.headers().contains_key("dav") ||
|
||||
response.headers().contains_key("DAV") ||
|
||||
response.status().is_success(),
|
||||
"Server doesn't appear to be a CalDAV server - missing DAV headers"
|
||||
);
|
||||
|
||||
println!("✓ Authentication test passed!");
|
||||
}
|
||||
|
||||
/// Test making a PROPFIND request to discover calendars
|
||||
///
|
||||
/// This test requires a valid .env file and makes an actual CalDAV PROPFIND request
|
||||
///
|
||||
/// Run with: `cargo test test_propfind_calendars`
|
||||
#[tokio::test]
|
||||
async fn test_propfind_calendars() {
|
||||
let config = CalDAVConfig::from_env()
|
||||
.expect("Failed to load CalDAV config from environment");
|
||||
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
// CalDAV PROPFIND request to discover calendars
|
||||
let propfind_body = r#"<?xml version="1.0" encoding="utf-8" ?>
|
||||
<d:propfind xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
|
||||
<d:prop>
|
||||
<d:resourcetype />
|
||||
<d:displayname />
|
||||
<c:calendar-description />
|
||||
<c:supported-calendar-component-set />
|
||||
</d:prop>
|
||||
</d:propfind>"#;
|
||||
|
||||
let response = client
|
||||
.request(reqwest::Method::from_bytes(b"PROPFIND").unwrap(), &config.server_url)
|
||||
.header("Authorization", format!("Basic {}", config.get_basic_auth()))
|
||||
.header("Content-Type", "application/xml")
|
||||
.header("Depth", "1")
|
||||
.header("User-Agent", "calendar-app/0.1.0")
|
||||
.body(propfind_body)
|
||||
.send()
|
||||
.await
|
||||
.expect("Failed to send PROPFIND request");
|
||||
|
||||
let status = response.status();
|
||||
println!("PROPFIND Response status: {}", status);
|
||||
|
||||
let body = response.text().await.expect("Failed to read response body");
|
||||
println!("PROPFIND Response body: {}", body);
|
||||
|
||||
// We should get a 207 Multi-Status for PROPFIND
|
||||
assert_eq!(
|
||||
status,
|
||||
reqwest::StatusCode::from_u16(207).unwrap(),
|
||||
"PROPFIND should return 207 Multi-Status"
|
||||
);
|
||||
|
||||
// The response should contain XML with calendar information
|
||||
assert!(body.contains("calendar"), "Response should contain calendar information");
|
||||
|
||||
println!("✓ PROPFIND calendars test passed!");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user