Start to implement OpenID authentication
This commit is contained in:
parent
48f24e6ca1
commit
d8946eb462
47
remote_backend/Cargo.lock
generated
47
remote_backend/Cargo.lock
generated
@ -58,6 +58,22 @@ dependencies = [
|
|||||||
"zstd",
|
"zstd",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-identity"
|
||||||
|
version = "0.7.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2c99b7a5614b72a78f04aa2021e5370fc1aef2475fffeffc0c1266b99007062"
|
||||||
|
dependencies = [
|
||||||
|
"actix-service",
|
||||||
|
"actix-session",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"derive_more",
|
||||||
|
"futures-core",
|
||||||
|
"serde",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-macros"
|
name = "actix-macros"
|
||||||
version = "0.2.4"
|
version = "0.2.4"
|
||||||
@ -130,6 +146,22 @@ dependencies = [
|
|||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "actix-session"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b671404ec72194d8af58c2bdaf51e3c477a0595056bd5010148405870dda8df2"
|
||||||
|
dependencies = [
|
||||||
|
"actix-service",
|
||||||
|
"actix-utils",
|
||||||
|
"actix-web",
|
||||||
|
"anyhow",
|
||||||
|
"derive_more",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"tracing",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "actix-utils"
|
name = "actix-utils"
|
||||||
version = "3.0.1"
|
version = "3.0.1"
|
||||||
@ -360,6 +392,12 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "base64"
|
||||||
|
version = "0.20.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ea22880d78093b0cbe17c89f64a7d457941e65759157ec6cb31a31d652b05e5"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "base64"
|
name = "base64"
|
||||||
version = "0.21.7"
|
version = "0.21.7"
|
||||||
@ -565,7 +603,14 @@ version = "0.16.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
checksum = "e859cd57d0710d9e06c381b550c06e76992472a8c6d527aecd2fc673dcc231fb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"aes-gcm",
|
||||||
|
"base64 0.20.0",
|
||||||
|
"hkdf",
|
||||||
|
"hmac",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
|
"rand",
|
||||||
|
"sha2",
|
||||||
|
"subtle",
|
||||||
"time",
|
"time",
|
||||||
"version_check",
|
"version_check",
|
||||||
]
|
]
|
||||||
@ -1646,12 +1691,14 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
|
|||||||
name = "remote_backend"
|
name = "remote_backend"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"actix-identity",
|
||||||
"actix-remote-ip",
|
"actix-remote-ip",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"basic-jwt",
|
"basic-jwt",
|
||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
|
"futures-util",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"light-openid",
|
"light-openid",
|
||||||
"log",
|
"log",
|
||||||
|
@ -12,10 +12,12 @@ clap = { version = "4.5.4", features = ["derive", "env"] }
|
|||||||
serde = { version = "1.0.198", features = ["derive"] }
|
serde = { version = "1.0.198", features = ["derive"] }
|
||||||
light-openid = { version = "1.0.2", features = ["crypto-wrapper"] }
|
light-openid = { version = "1.0.2", features = ["crypto-wrapper"] }
|
||||||
basic-jwt = "0.2.0"
|
basic-jwt = "0.2.0"
|
||||||
actix-remote-ip = "0.1.0"
|
|
||||||
lazy_static = "1.4.0"
|
|
||||||
actix-web = "4.5.1"
|
actix-web = "4.5.1"
|
||||||
|
actix-remote-ip = "0.1.0"
|
||||||
|
actix-identity = "0.7.1"
|
||||||
|
lazy_static = "1.4.0"
|
||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
reqwest = { version = "0.12.4", features = ["json"] }
|
reqwest = { version = "0.12.4", features = ["json"] }
|
||||||
thiserror = "1.0.59"
|
thiserror = "1.0.59"
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.8.0", features = ["v4"] }
|
||||||
|
futures-util = "0.3.30"
|
@ -33,9 +33,9 @@ pub struct AppConfig {
|
|||||||
)]
|
)]
|
||||||
pub oidc_configuration_url: String,
|
pub oidc_configuration_url: String,
|
||||||
|
|
||||||
/// Disable OpenID authentication
|
/// Disable authentication (for development purposes ONLY)
|
||||||
#[arg(long, env)]
|
#[arg(long, env)]
|
||||||
pub disable_oidc: bool,
|
pub unsecure_disable_login: bool,
|
||||||
|
|
||||||
/// OpenID client ID
|
/// OpenID client ID
|
||||||
#[arg(long, env, default_value = "foo")]
|
#[arg(long, env, default_value = "foo")]
|
||||||
|
5
remote_backend/src/constants.rs
Normal file
5
remote_backend/src/constants.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub const ROUTES_WITHOUT_AUTH: [&str; 3] = [
|
||||||
|
"/api/server/config",
|
||||||
|
"/api/auth/start_oidc",
|
||||||
|
"/api/auth/finish_oidc",
|
||||||
|
];
|
97
remote_backend/src/controllers/auth_controller.rs
Normal file
97
remote_backend/src/controllers/auth_controller.rs
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
use actix_remote_ip::RemoteIP;
|
||||||
|
use actix_web::web::Data;
|
||||||
|
use actix_web::{web, HttpResponse, Responder};
|
||||||
|
use light_openid::basic_state_manager::BasicStateManager;
|
||||||
|
|
||||||
|
use crate::app_config::AppConfig;
|
||||||
|
use crate::controllers::HttpResult;
|
||||||
|
use crate::extractors::auth_extractor::AuthExtractor;
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
struct StartOIDCResponse {
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Start OIDC authentication
|
||||||
|
pub async fn start_oidc(sm: Data<BasicStateManager>, ip: RemoteIP) -> HttpResult {
|
||||||
|
let prov = AppConfig::get().openid_provider();
|
||||||
|
|
||||||
|
let conf =
|
||||||
|
light_openid::primitives::OpenIDConfig::load_from_url(prov.configuration_url).await?;
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(StartOIDCResponse {
|
||||||
|
url: conf.gen_authorization_url(
|
||||||
|
prov.client_id,
|
||||||
|
&sm.gen_state(ip.0)?,
|
||||||
|
&AppConfig::get().oidc_redirect_url(),
|
||||||
|
),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct FinishOpenIDLoginQuery {
|
||||||
|
code: String,
|
||||||
|
state: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finish OIDC authentication
|
||||||
|
pub async fn finish_oidc(
|
||||||
|
sm: Data<BasicStateManager>,
|
||||||
|
remote_ip: RemoteIP,
|
||||||
|
req: web::Json<FinishOpenIDLoginQuery>,
|
||||||
|
auth: AuthExtractor,
|
||||||
|
) -> HttpResult {
|
||||||
|
if let Err(e) = sm.validate_state(remote_ip.0, &req.state) {
|
||||||
|
log::error!("Failed to validate OIDC CB state! {e}");
|
||||||
|
return Ok(HttpResponse::BadRequest().json("Invalid state!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let prov = AppConfig::get().openid_provider();
|
||||||
|
|
||||||
|
let conf =
|
||||||
|
light_openid::primitives::OpenIDConfig::load_from_url(prov.configuration_url).await?;
|
||||||
|
|
||||||
|
let (token, _) = conf
|
||||||
|
.request_token(
|
||||||
|
prov.client_id,
|
||||||
|
prov.client_secret,
|
||||||
|
&req.code,
|
||||||
|
&AppConfig::get().oidc_redirect_url(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
let (user_info, _) = conf.request_user_info(&token).await?;
|
||||||
|
|
||||||
|
if user_info.email_verified != Some(true) {
|
||||||
|
log::error!("Email is not verified!");
|
||||||
|
return Ok(HttpResponse::Unauthorized().json("Email unverified by IDP!"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mail = match user_info.email {
|
||||||
|
Some(m) => m,
|
||||||
|
None => {
|
||||||
|
return Ok(HttpResponse::Unauthorized().json("Email not provided by the IDP!"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
auth.authenticate(mail);
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().finish())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Serialize)]
|
||||||
|
struct CurrentUser {
|
||||||
|
id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get current authenticated user
|
||||||
|
pub async fn current_user(auth: AuthExtractor) -> impl Responder {
|
||||||
|
HttpResponse::Ok().json(CurrentUser {
|
||||||
|
id: auth.id().unwrap_or_else(|| "Anonymous".to_string()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign out
|
||||||
|
pub async fn sign_out(auth: AuthExtractor) -> impl Responder {
|
||||||
|
auth.sign_out();
|
||||||
|
HttpResponse::Ok().finish()
|
||||||
|
}
|
89
remote_backend/src/controllers/mod.rs
Normal file
89
remote_backend/src/controllers/mod.rs
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
use actix_web::body::BoxBody;
|
||||||
|
use actix_web::http::StatusCode;
|
||||||
|
use actix_web::HttpResponse;
|
||||||
|
use std::error::Error;
|
||||||
|
use std::fmt::{Display, Formatter};
|
||||||
|
use std::io::ErrorKind;
|
||||||
|
|
||||||
|
pub mod auth_controller;
|
||||||
|
|
||||||
|
/// Custom error to ease controller writing
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum HttpErr {
|
||||||
|
Err(anyhow::Error),
|
||||||
|
HTTPResponse(HttpResponse),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for HttpErr {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
HttpErr::Err(err) => Display::fmt(err, f),
|
||||||
|
HttpErr::HTTPResponse(res) => {
|
||||||
|
Display::fmt(&format!("HTTP RESPONSE {}", res.status().as_str()), f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl actix_web::error::ResponseError for HttpErr {
|
||||||
|
fn status_code(&self) -> StatusCode {
|
||||||
|
match self {
|
||||||
|
HttpErr::Err(_) => StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
HttpErr::HTTPResponse(r) => r.status(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn error_response(&self) -> HttpResponse<BoxBody> {
|
||||||
|
log::error!("Error while processing request! {}", self);
|
||||||
|
|
||||||
|
HttpResponse::InternalServerError().body("Failed to execute request!")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<anyhow::Error> for HttpErr {
|
||||||
|
fn from(err: anyhow::Error) -> HttpErr {
|
||||||
|
HttpErr::Err(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Box<dyn Error>> for HttpErr {
|
||||||
|
fn from(value: Box<dyn Error>) -> Self {
|
||||||
|
HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::io::Error> for HttpErr {
|
||||||
|
fn from(value: std::io::Error) -> Self {
|
||||||
|
HttpErr::Err(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<std::num::ParseIntError> for HttpErr {
|
||||||
|
fn from(value: std::num::ParseIntError) -> Self {
|
||||||
|
HttpErr::Err(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::Error> for HttpErr {
|
||||||
|
fn from(value: reqwest::Error) -> Self {
|
||||||
|
HttpErr::Err(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<reqwest::header::ToStrError> for HttpErr {
|
||||||
|
fn from(value: reqwest::header::ToStrError) -> Self {
|
||||||
|
HttpErr::Err(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<actix_web::Error> for HttpErr {
|
||||||
|
fn from(value: actix_web::Error) -> Self {
|
||||||
|
HttpErr::Err(std::io::Error::new(ErrorKind::Other, value.to_string()).into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<HttpResponse> for HttpErr {
|
||||||
|
fn from(value: HttpResponse) -> Self {
|
||||||
|
HttpErr::HTTPResponse(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub type HttpResult = Result<HttpResponse, HttpErr>;
|
47
remote_backend/src/extractors/auth_extractor.rs
Normal file
47
remote_backend/src/extractors/auth_extractor.rs
Normal file
@ -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 AuthExtractor {
|
||||||
|
identity: Option<Identity>,
|
||||||
|
request: HttpRequest,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AuthExtractor {
|
||||||
|
/// 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, id: impl Display) {
|
||||||
|
Identity::login(&self.request.extensions(), id.to_string())
|
||||||
|
.expect("Unable to set authentication!");
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn id(&self) -> Option<String> {
|
||||||
|
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 AuthExtractor {
|
||||||
|
type Error = Error;
|
||||||
|
type Future = Ready<Result<Self, Error>>;
|
||||||
|
|
||||||
|
fn from_request(req: &HttpRequest, payload: &mut Payload) -> Self::Future {
|
||||||
|
let identity: Option<Identity> = Identity::from_request(req, payload).into_inner().ok();
|
||||||
|
|
||||||
|
ready(Ok(Self {
|
||||||
|
identity,
|
||||||
|
request: req.clone(),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
1
remote_backend/src/extractors/mod.rs
Normal file
1
remote_backend/src/extractors/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod auth_extractor;
|
@ -1,3 +1,7 @@
|
|||||||
pub mod app_config;
|
pub mod app_config;
|
||||||
|
pub mod constants;
|
||||||
|
pub mod controllers;
|
||||||
|
pub mod extractors;
|
||||||
|
pub mod middlewares;
|
||||||
pub mod utils;
|
pub mod utils;
|
||||||
pub mod virtweb_client;
|
pub mod virtweb_client;
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
use actix_remote_ip::RemoteIPConfig;
|
use actix_remote_ip::RemoteIPConfig;
|
||||||
use actix_web::middleware::Logger;
|
use actix_web::middleware::Logger;
|
||||||
use actix_web::web::Data;
|
use actix_web::web::Data;
|
||||||
use actix_web::{App, HttpServer};
|
use actix_web::{web, App, HttpServer};
|
||||||
use light_openid::basic_state_manager::BasicStateManager;
|
use light_openid::basic_state_manager::BasicStateManager;
|
||||||
use remote_backend::app_config::AppConfig;
|
use remote_backend::app_config::AppConfig;
|
||||||
|
use remote_backend::controllers::auth_controller;
|
||||||
use remote_backend::virtweb_client;
|
use remote_backend::virtweb_client;
|
||||||
|
|
||||||
#[actix_web::main]
|
#[actix_web::main]
|
||||||
@ -21,6 +22,22 @@ async fn main() -> std::io::Result<()> {
|
|||||||
.app_data(Data::new(RemoteIPConfig {
|
.app_data(Data::new(RemoteIPConfig {
|
||||||
proxy: AppConfig::get().proxy_ip.clone(),
|
proxy: AppConfig::get().proxy_ip.clone(),
|
||||||
}))
|
}))
|
||||||
|
.route(
|
||||||
|
"/api/auth/start_oidc",
|
||||||
|
web::get().to(auth_controller::start_oidc),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/auth/finish_oidc",
|
||||||
|
web::post().to(auth_controller::finish_oidc),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/auth/user",
|
||||||
|
web::get().to(auth_controller::current_user),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/api/auth/sign_out",
|
||||||
|
web::get().to(auth_controller::sign_out),
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.bind(&AppConfig::get().listen_address)?
|
.bind(&AppConfig::get().listen_address)?
|
||||||
.run()
|
.run()
|
||||||
|
111
remote_backend/src/middlewares/auth_middleware.rs
Normal file
111
remote_backend/src/middlewares/auth_middleware.rs
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
use std::future::{ready, Ready};
|
||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::app_config::AppConfig;
|
||||||
|
use crate::constants;
|
||||||
|
use crate::extractors::auth_extractor::AuthExtractor;
|
||||||
|
use actix_web::body::EitherBody;
|
||||||
|
use actix_web::dev::Payload;
|
||||||
|
use actix_web::{
|
||||||
|
dev::{forward_ready, Service, ServiceRequest, ServiceResponse, Transform},
|
||||||
|
Error, FromRequest, HttpResponse,
|
||||||
|
};
|
||||||
|
use futures_util::future::LocalBoxFuture;
|
||||||
|
|
||||||
|
// There are two steps in middleware processing.
|
||||||
|
// 1. Middleware initialization, middleware factory gets called with
|
||||||
|
// next service in chain as parameter.
|
||||||
|
// 2. Middleware's call method gets called with normal request.
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct AuthChecker;
|
||||||
|
|
||||||
|
// Middleware factory is `Transform` trait
|
||||||
|
// `S` - type of the next service
|
||||||
|
// `B` - type of response's body
|
||||||
|
impl<S, B> Transform<S, ServiceRequest> for AuthChecker
|
||||||
|
where
|
||||||
|
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
B: 'static,
|
||||||
|
{
|
||||||
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
|
type Error = Error;
|
||||||
|
type Transform = AuthMiddleware<S>;
|
||||||
|
type InitError = ();
|
||||||
|
type Future = Ready<Result<Self::Transform, Self::InitError>>;
|
||||||
|
|
||||||
|
fn new_transform(&self, service: S) -> Self::Future {
|
||||||
|
ready(Ok(AuthMiddleware {
|
||||||
|
service: Rc::new(service),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AuthMiddleware<S> {
|
||||||
|
service: Rc<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S, B> Service<ServiceRequest> for AuthMiddleware<S>
|
||||||
|
where
|
||||||
|
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error> + 'static,
|
||||||
|
S::Future: 'static,
|
||||||
|
B: 'static,
|
||||||
|
{
|
||||||
|
type Response = ServiceResponse<EitherBody<B>>;
|
||||||
|
type Error = Error;
|
||||||
|
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
|
forward_ready!(service);
|
||||||
|
|
||||||
|
fn call(&self, req: ServiceRequest) -> Self::Future {
|
||||||
|
let service = Rc::clone(&self.service);
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let remote_ip =
|
||||||
|
actix_remote_ip::RemoteIP::from_request(req.request(), &mut Payload::None)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// Check if no authentication is required
|
||||||
|
if constants::ROUTES_WITHOUT_AUTH.contains(&req.path())
|
||||||
|
|| !req.path().starts_with("/api/")
|
||||||
|
{
|
||||||
|
log::trace!("No authentication is required")
|
||||||
|
}
|
||||||
|
// Check cookie authentication
|
||||||
|
else if !&AppConfig::get().unsecure_disable_login {
|
||||||
|
let auth = match AuthExtractor::from_request(req.request(), &mut Payload::None)
|
||||||
|
.into_inner()
|
||||||
|
{
|
||||||
|
Ok(auth) => auth,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!(
|
||||||
|
"Failed to extract authentication information from request! {e}"
|
||||||
|
);
|
||||||
|
return Ok(req
|
||||||
|
.into_response(HttpResponse::PreconditionFailed().finish())
|
||||||
|
.map_into_right_body());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if !auth.is_authenticated() {
|
||||||
|
log::error!(
|
||||||
|
"User attempted to access privileged route without authentication!"
|
||||||
|
);
|
||||||
|
return Ok(req
|
||||||
|
.into_response(
|
||||||
|
HttpResponse::PreconditionFailed().json("Please authenticate!"),
|
||||||
|
)
|
||||||
|
.map_into_right_body());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
log::info!("{} - {}", remote_ip.0, req.path());
|
||||||
|
|
||||||
|
service
|
||||||
|
.call(req)
|
||||||
|
.await
|
||||||
|
.map(ServiceResponse::map_into_left_body)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
1
remote_backend/src/middlewares/mod.rs
Normal file
1
remote_backend/src/middlewares/mod.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod auth_middleware;
|
Loading…
Reference in New Issue
Block a user