Files
calendar/backend/src/handlers/auth.rs
Connor Johnstone 0821573041 Remove remaining verbose CalDAV discovery and authentication logs
- Remove discovery response XML dumps that flood console
- Remove calendar collection checking logs
- Remove authentication success messages
- Remove API call password length logging
- Fix unused variable warning

Backend now runs with minimal essential logging only.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-18 16:15:30 -04:00

141 lines
4.4 KiB
Rust

use axum::{extract::State, http::HeaderMap, response::Json};
use std::sync::Arc;
use crate::calendar::CalDAVClient;
use crate::{
models::{ApiError, AuthResponse, CalDAVLoginRequest, CalendarInfo, UserInfo},
AppState,
};
pub fn extract_bearer_token(headers: &HeaderMap) -> Result<String, ApiError> {
let auth_header = headers
.get("authorization")
.ok_or_else(|| ApiError::Unauthorized("Missing Authorization header".to_string()))?;
let auth_str = auth_header
.to_str()
.map_err(|_| ApiError::BadRequest("Invalid Authorization header".to_string()))?;
if let Some(token) = auth_str.strip_prefix("Bearer ") {
Ok(token.to_string())
} else {
Err(ApiError::BadRequest(
"Authorization header must be Bearer token".to_string(),
))
}
}
pub fn extract_password_header(headers: &HeaderMap) -> Result<String, ApiError> {
let password_header = headers
.get("x-caldav-password")
.ok_or_else(|| ApiError::BadRequest("Missing X-CalDAV-Password header".to_string()))?;
password_header
.to_str()
.map(|s| s.to_string())
.map_err(|_| ApiError::BadRequest("Invalid X-CalDAV-Password header".to_string()))
}
pub async fn login(
State(state): State<Arc<AppState>>,
Json(request): Json<CalDAVLoginRequest>,
) -> Result<Json<AuthResponse>, ApiError> {
println!("🔐 Login attempt:");
println!(" Server URL: {}", request.server_url);
println!(" Username: {}", request.username);
println!(" Password length: {}", request.password.len());
// Use the auth service login method which now handles database, sessions, and preferences
let response = state.auth_service.login(request).await?;
println!("✅ Login successful with session management");
Ok(Json(response))
}
pub async fn verify_token(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> Result<Json<serde_json::Value>, ApiError> {
let token = extract_bearer_token(&headers)?;
let is_valid = state.auth_service.verify_token(&token).is_ok();
Ok(Json(serde_json::json!({ "valid": is_valid })))
}
pub async fn get_user_info(
State(state): State<Arc<AppState>>,
headers: HeaderMap,
) -> Result<Json<UserInfo>, ApiError> {
let token = extract_bearer_token(&headers)?;
let password = extract_password_header(&headers)?;
// Create CalDAV config from token and password
let config = state
.auth_service
.caldav_config_from_token(&token, &password)?;
let client = CalDAVClient::new(config.clone());
// Discover calendars
let calendar_paths = client
.discover_calendars()
.await
.map_err(|e| ApiError::Internal(format!("Failed to discover calendars: {}", e)))?;
let calendars: Vec<CalendarInfo> = calendar_paths
.iter()
.map(|path| CalendarInfo {
path: path.clone(),
display_name: extract_calendar_name(path),
color: generate_calendar_color(path),
is_visible: true, // Default to visible
})
.collect();
Ok(Json(UserInfo {
username: config.username,
server_url: config.server_url,
calendars,
}))
}
fn generate_calendar_color(path: &str) -> String {
// Generate a consistent color based on the calendar path
// This is a simple hash-based approach
let mut hash: u32 = 0;
for byte in path.bytes() {
hash = hash.wrapping_mul(31).wrapping_add(byte as u32);
}
// Define a set of pleasant colors
let colors = [
"#3B82F6", "#10B981", "#F59E0B", "#EF4444", "#8B5CF6", "#06B6D4", "#84CC16", "#F97316",
"#EC4899", "#6366F1", "#14B8A6", "#F3B806", "#8B5A2B", "#6B7280", "#DC2626", "#7C3AED",
"#059669", "#D97706", "#BE185D", "#4F46E5",
];
colors[(hash as usize) % colors.len()].to_string()
}
fn extract_calendar_name(path: &str) -> String {
// Extract calendar name from path
// E.g., "/calendars/user/calendar-name/" -> "Calendar Name"
path.split('/')
.filter(|s| !s.is_empty())
.last()
.unwrap_or("Calendar")
.replace('-', " ")
.replace('_', " ")
.split_whitespace()
.map(|word| {
let mut chars = word.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
})
.collect::<Vec<String>>()
.join(" ")
}