diff --git a/src/data/app_config.rs b/src/data/app_config.rs index 7350128..ef8464a 100644 --- a/src/data/app_config.rs +++ b/src/data/app_config.rs @@ -5,7 +5,7 @@ use clap::Parser; use crate::constants::USERS_LIST_FILE; /// Basic OIDC provider -#[derive(Parser, Debug)] +#[derive(Parser, Debug, Clone)] #[clap(author, version, about, long_about = None)] pub struct AppConfig { /// Listen address @@ -20,12 +20,16 @@ pub struct AppConfig { #[clap(short, long, env, default_value = "")] pub token_key: String, - /// Should the auth cookie be secure - #[clap(long, env)] - pub secure_auth_cookie: bool, + /// Website origin + #[clap(short, long, env, default_value = "http://localhost:8000")] + pub website_origin: String, } impl AppConfig { + pub fn secure_cookie(&self) -> bool { + self.website_origin.starts_with("https:") + } + pub fn storage_path(&self) -> &Path { self.storage_path.as_ref() } diff --git a/src/main.rs b/src/main.rs index 163e096..2bf333a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -62,11 +62,12 @@ async fn main() -> std::io::Result<()> { let users_actor = UsersActor::new(users).start(); log::info!("Server will listen on {}", config.listen_address); + let listen_address = config.listen_address.to_string(); HttpServer::new(move || { let policy = CookieIdentityPolicy::new(config.token_key.as_bytes()) .name(SESSION_COOKIE_NAME) - .secure(config.secure_auth_cookie) + .secure(config.secure_cookie()) .visit_deadline(Duration::seconds(MAX_INACTIVITY_DURATION)) .login_deadline(Duration::seconds(MAX_SESSION_DURATION)) .same_site(SameSite::Strict); @@ -74,6 +75,7 @@ async fn main() -> std::io::Result<()> { App::new() .app_data(web::Data::new(users_actor.clone())) + .app_data(web::Data::new(config.clone())) .wrap(Logger::default()) .wrap(AuthMiddleware {}) @@ -92,7 +94,7 @@ async fn main() -> std::io::Result<()> { // Logout page .route("/logout", web::get().to(logout_route)) }) - .bind(config.listen_address)? + .bind(listen_address)? .run() .await } diff --git a/src/middlewares/auth_middleware.rs b/src/middlewares/auth_middleware.rs index 2bc1571..b281bc4 100644 --- a/src/middlewares/auth_middleware.rs +++ b/src/middlewares/auth_middleware.rs @@ -5,12 +5,14 @@ use std::pin::Pin; use std::rc::Rc; use actix_identity::RequestIdentity; -use actix_web::{dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, Error, HttpResponse}; +use actix_web::{dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform}, Error, HttpResponse, web}; use actix_web::body::EitherBody; +use actix_web::http::{header, Method}; use askama::Template; use crate::constants::{ADMIN_ROUTES, AUTHENTICATED_ROUTES}; use crate::controllers::base_controller::redirect_user_for_login; +use crate::data::app_config::AppConfig; use crate::data::session_identity::{SessionIdentity, SessionIdentityData, SessionStatus}; // There are two steps in middleware processing. @@ -83,6 +85,23 @@ impl Service for AuthInnerMiddleware // Forward request Box::pin(async move { + let config: &web::Data = req.app_data().expect("AppData undefined!"); + + // Check if POST request comes from another website (block invalid origins) + let origin = req.headers().get(header::ORIGIN); + if req.method() == Method::POST { + if let Some(o) = origin { + if !o.to_str().unwrap_or("bad").eq(&config.website_origin) { + log::warn!("Blocked POST request from invalid origin! Origin given {:?}", o); + return Ok(req.into_response( + HttpResponse::Unauthorized() + .body("POST request from invalid origin!") + .map_into_right_body() + )); + } + } + } + if req.path().starts_with("/.git") { return Ok(req.into_response( HttpResponse::Unauthorized() @@ -91,14 +110,14 @@ impl Service for AuthInnerMiddleware )); } - let identity = match SessionIdentity::deserialize_session_data(req.get_identity()) { + let session = match SessionIdentity::deserialize_session_data(req.get_identity()) { Some(SessionIdentityData { status: SessionStatus::SignedIn, is_admin: true, .. }) => ConnStatus::Admin, Some(SessionIdentityData { status: SessionStatus::SignedIn, .. }) => ConnStatus::RegularUser, _ => ConnStatus::SignedOut, }; // Redirect user to login page - if !identity.is_auth() && (req.path().starts_with(ADMIN_ROUTES) || + if !session.is_auth() && (req.path().starts_with(ADMIN_ROUTES) || req.path().starts_with(AUTHENTICATED_ROUTES)) { let path = req.path().to_string(); return Ok(req.into_response(redirect_user_for_login(path)) @@ -106,7 +125,7 @@ impl Service for AuthInnerMiddleware } // Restrict access to admin pages - if !identity.is_admin() && req.path().starts_with(ADMIN_ROUTES) { + if !session.is_admin() && req.path().starts_with(ADMIN_ROUTES) { return Ok(req.into_response(HttpResponse::Unauthorized() .body(AccessDeniedTemplate {}.render().unwrap())) .map_into_right_body());