diff --git a/virtweb_backend/src/app_config.rs b/virtweb_backend/src/app_config.rs index 76f553a..ea36e26 100644 --- a/virtweb_backend/src/app_config.rs +++ b/virtweb_backend/src/app_config.rs @@ -96,6 +96,11 @@ impl AppConfig { secret } + /// Check out whether provided credentials are valid or not for local authentication + pub fn check_local_login(&self, user: &str, pass: &str) -> bool { + self.auth_username == user && self.auth_password == pass + } + /// Get OpenID providers configuration pub fn openid_provider(&self) -> Option> { if self.disable_oidc { diff --git a/virtweb_backend/src/controllers/auth_controller.rs b/virtweb_backend/src/controllers/auth_controller.rs new file mode 100644 index 0000000..5191119 --- /dev/null +++ b/virtweb_backend/src/controllers/auth_controller.rs @@ -0,0 +1,31 @@ +use crate::app_config::AppConfig; +use crate::extractors::auth_extractor::AuthChecker; +use crate::extractors::local_auth_extractor::LocalAuthEnabled; +use actix_web::{web, HttpResponse, Responder}; + +#[derive(serde::Deserialize)] +pub struct LocalAuthReq { + username: String, + password: String, +} + +/// Perform local authentication +pub async fn local_auth( + local_auth_enabled: LocalAuthEnabled, + req: web::Json, + auth: AuthChecker, +) -> impl Responder { + if !*local_auth_enabled { + log::error!("Local auth attempt while this authentication method is disabled!"); + return HttpResponse::UnprocessableEntity().json("Local authentication is disabled!"); + } + + if !AppConfig::get().check_local_login(&req.username, &req.password) { + log::error!("Local auth attempt with invalid credentials!"); + return HttpResponse::Unauthorized().json("Invalid credentials!"); + } + + auth.authenticate(&req.username); + + HttpResponse::Accepted().json("Welcome") +} diff --git a/virtweb_backend/src/controllers/mod.rs b/virtweb_backend/src/controllers/mod.rs index 98a0949..ad755c3 100644 --- a/virtweb_backend/src/controllers/mod.rs +++ b/virtweb_backend/src/controllers/mod.rs @@ -1 +1,2 @@ +pub mod auth_controller; pub mod server_controller; diff --git a/virtweb_backend/src/extractors/auth_extractor.rs b/virtweb_backend/src/extractors/auth_extractor.rs new file mode 100644 index 0000000..eea58b2 --- /dev/null +++ b/virtweb_backend/src/extractors/auth_extractor.rs @@ -0,0 +1,47 @@ +use actix_identity::Identity; +use actix_web::dev::Payload; +use actix_web::{Error, FromRequest, HttpMessage, HttpRequest}; +use futures_util::future::{ready, Ready}; +use std::fmt::Display; + +pub struct AuthChecker { + identity: Option, + request: HttpRequest, +} + +impl AuthChecker { + /// Check whether the user is authenticated or not + pub fn is_authenticated(&self) -> bool { + self.identity.is_some() + } + + /// Authenticate the user + pub fn authenticate(&self, username: impl Display) { + Identity::login(&self.request.extensions(), username.to_string()) + .expect("Unable to set authentication!"); + } + + pub fn user_name(&self) -> Option { + self.identity.as_ref().map(|i| i.id().unwrap()) + } + + pub fn sign_out(self) { + if let Some(i) = self.identity { + i.logout() + } + } +} + +impl FromRequest for AuthChecker { + type Error = Error; + type Future = Ready>; + + fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future { + let identity: Option = Identity::from_request(req, payload).into_inner().ok(); + + ready(Ok(Self { + identity, + request: req.clone(), + })) + } +} diff --git a/virtweb_backend/src/extractors/local_auth_extractor.rs b/virtweb_backend/src/extractors/local_auth_extractor.rs index cc3fbbb..9933f26 100644 --- a/virtweb_backend/src/extractors/local_auth_extractor.rs +++ b/virtweb_backend/src/extractors/local_auth_extractor.rs @@ -28,6 +28,6 @@ impl FromRequest for LocalAuthEnabled { .headers() .get(&AppConfig::get().disable_auth_header_token) .is_some(); - return ready(Ok(Self(!has_disable_local_auth_header))); + ready(Ok(Self(!has_disable_local_auth_header))) } } diff --git a/virtweb_backend/src/extractors.rs b/virtweb_backend/src/extractors/mod.rs similarity index 55% rename from virtweb_backend/src/extractors.rs rename to virtweb_backend/src/extractors/mod.rs index c472199..d7e49e1 100644 --- a/virtweb_backend/src/extractors.rs +++ b/virtweb_backend/src/extractors/mod.rs @@ -1 +1,2 @@ +pub mod auth_extractor; pub mod local_auth_extractor; diff --git a/virtweb_backend/src/main.rs b/virtweb_backend/src/main.rs index 12c9378..f9e4aa0 100644 --- a/virtweb_backend/src/main.rs +++ b/virtweb_backend/src/main.rs @@ -11,7 +11,7 @@ use virtweb_backend::app_config::AppConfig; use virtweb_backend::constants::{ MAX_INACTIVITY_DURATION, MAX_SESSION_DURATION, SESSION_COOKIE_NAME, }; -use virtweb_backend::controllers::server_controller; +use virtweb_backend::controllers::{auth_controller, server_controller}; #[actix_web::main] async fn main() -> std::io::Result<()> { @@ -42,11 +42,17 @@ async fn main() -> std::io::Result<()> { .app_data(web::Data::new(RemoteIPConfig { proxy: AppConfig::get().proxy_ip.clone(), })) + // Server controller .route("/", web::get().to(server_controller::root_index)) .route( "/api/server/static_config", web::get().to(server_controller::static_config), ) + // Auth controller + .route( + "/api/auth/local", + web::post().to(auth_controller::local_auth), + ) }) .bind(&AppConfig::get().listen_address)? .run()