diff --git a/src/handlers.rs b/src/handlers.rs index 58ba81f..69a2a89 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -23,6 +23,8 @@ use crate::views::{ type AppError = (StatusCode, String); const SESSION_COOKIE: &str = "session_token"; const SESSION_SECONDS: i64 = 7 * 24 * 60 * 60; +const MIN_ALLOWED_YEAR: i32 = 2000; +const MAX_ALLOWED_YEAR: i32 = 3000; #[derive(Clone)] pub struct AppState { @@ -64,6 +66,7 @@ pub async fn show_calendar_for_month( let Some(auth) = auth else { return Ok(Redirect::to("/login").into_response()); }; + validate_year_range(year)?; let today = user_local_today(&auth.user.timezone); let focus_date = NaiveDate::from_ymd_opt(year, month, 1).ok_or(( StatusCode::BAD_REQUEST, @@ -1450,14 +1453,30 @@ fn calendar_path(date: NaiveDate) -> String { } fn validate_date(date_text: &str) -> Result<(), AppError> { - NaiveDate::parse_from_str(date_text, "%Y-%m-%d") - .map(|_| ()) - .map_err(|_| { - ( - StatusCode::BAD_REQUEST, - "Date must be YYYY-MM-DD".to_string(), - ) - }) + let date = NaiveDate::parse_from_str(date_text, "%Y-%m-%d").map_err(|_| { + ( + StatusCode::BAD_REQUEST, + format!( + "Date must be YYYY-MM-DD with year between {} and {}", + MIN_ALLOWED_YEAR, MAX_ALLOWED_YEAR + ), + ) + })?; + validate_year_range(date.year()) +} + +fn validate_year_range(year: i32) -> Result<(), AppError> { + if (MIN_ALLOWED_YEAR..=MAX_ALLOWED_YEAR).contains(&year) { + Ok(()) + } else { + Err(( + StatusCode::BAD_REQUEST, + format!( + "Year must be between {} and {}", + MIN_ALLOWED_YEAR, MAX_ALLOWED_YEAR + ), + )) + } } fn parse_entry_form_fields(form: &HashMap) -> Result<(String, i64), AppError> {