Skip to content

Commit

Permalink
Use unused port, use token auth
Browse files Browse the repository at this point in the history
  • Loading branch information
xcmd-io committed Oct 23, 2023
1 parent ddab883 commit 78d805d
Show file tree
Hide file tree
Showing 15 changed files with 228 additions and 101 deletions.
7 changes: 4 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion xcmd-base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ edition = "2021"

[dependencies]
actix-web = { version = "4.4", features = ["rustls"] }
parking_lot = "0.12.1"
actix-cors = "0.6"
futures-util = "0.3"
parking_lot = "0.12"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_derive = "1.0"
Expand Down
147 changes: 136 additions & 11 deletions xcmd-base/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,52 @@
mod telemetry;

use actix_cors::Cors;
use actix_web::{
dev::{Server, ServerHandle},
dev::{
forward_ready, Server, ServerHandle, Service, ServiceRequest, ServiceResponse, Transform,
},
http::header,
web::Data,
};
use futures_util::future::LocalBoxFuture;
use parking_lot::Mutex;
use serde::Deserialize;
use serde_derive::Serialize;
use std::{error::Error, thread, time::Duration};
use std::future::{ready, Ready};
use std::{env, error::Error, net::TcpListener, thread, time::Duration};
use sysinfo::{ProcessExt, System, SystemExt};

pub fn loop_while_parent_process_exists() -> Result<(), Box<dyn Error>> {
pub fn get_port() -> Result<u16, Box<dyn Error>> {
let port = if let Ok(port_str) = env::var("XCMD_PORT") {
port_str.parse::<u16>()?
} else {
get_unused_port()?
};

Ok(port)
}

fn get_unused_port() -> Result<u16, std::io::Error> {
let listener = TcpListener::bind(("127.0.0.1", 0))?;
let port = listener.local_addr()?.port();
drop(listener);
Ok(port)
}

pub fn post_startup(server: &Server, port: u16) {
let stop_handle = Data::new(StopHandle::default());
stop_handle.register(server.handle());

thread::spawn(move || {
loop_while_parent_process_exists().ok();
stop_handle.stop(true);
});

let value = StartupResponse { port };
println!("{}", serde_json::to_string(&value).unwrap());
}

fn loop_while_parent_process_exists() -> Result<(), Box<dyn Error>> {
let mut system = System::new();
let pid = sysinfo::get_current_pid()?;
if !system.refresh_process(pid) {
Expand All @@ -29,14 +65,10 @@ pub fn loop_while_parent_process_exists() -> Result<(), Box<dyn Error>> {
}
}

pub fn stop_server_when_parent_process_exits(server: &Server) {
let stop_handle = Data::new(StopHandle::default());
stop_handle.register(server.handle());

thread::spawn(move || {
loop_while_parent_process_exists().ok();
stop_handle.stop(true);
});
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct StartupResponse {
pub port: u16,
}

#[derive(Default)]
Expand Down Expand Up @@ -148,3 +180,96 @@ pub fn init_telemetry(app_name: &str) {
);
telemetry::init_subscriber(subscriber);
}

pub struct Middleware;

impl Middleware {
pub fn token_auth() -> TokenAuth {
TokenAuth::new(env::var("XCMD_TOKEN").ok())
}

pub fn cors() -> Cors {
Cors::default()
.allowed_origin("null")
.allowed_origin("tauri://localhost")
.allowed_origin("https://tauri.localhost")
.allowed_origin("http://tauri.localhost")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![
header::AUTHORIZATION,
header::ACCEPT,
header::CONTENT_TYPE,
])
.max_age(3600)
}
}

pub struct TokenAuth {
token: Option<String>,
}

impl TokenAuth {
pub fn new(token: Option<String>) -> Self {
TokenAuth { token }
}
}

impl<S, B> Transform<S, ServiceRequest> for TokenAuth
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = actix_web::Error;
type InitError = ();
type Transform = TokenAuthMiddleware<S>;
type Future = Ready<Result<Self::Transform, Self::InitError>>;

fn new_transform(&self, service: S) -> Self::Future {
ready(Ok(TokenAuthMiddleware {
service,
token: self.token.clone(),
}))
}
}

pub struct TokenAuthMiddleware<S> {
service: S,
token: Option<String>,
}

impl<S, B> Service<ServiceRequest> for TokenAuthMiddleware<S>
where
S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = actix_web::Error>,
S::Future: 'static,
B: 'static,
{
type Response = ServiceResponse<B>;
type Error = actix_web::Error;
type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;

forward_ready!(service);

fn call(&self, req: ServiceRequest) -> Self::Future {
if let Some(token) = &self.token {
if let Some(auth_header) = req.headers().get("Authorization") {
let auth_value = auth_header.to_str().unwrap_or_default();
if auth_value.starts_with("Bearer ") && &auth_value["Bearer ".len()..] != token {
return Box::pin(async move {
Err(actix_web::error::ErrorUnauthorized("Unauthorized"))
});
}
} else {
return Box::pin(async move {
Err(actix_web::error::ErrorUnauthorized("Unauthorized"))
});
}
}

// return Box::pin(async move { Err(actix_web::error::ErrorUnauthorized("Unauthorized")) });
let fut = self.service.call(req);

Box::pin(async move { Ok(fut.await?) })
}
}
4 changes: 2 additions & 2 deletions xcmd-base/src/telemetry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ pub fn get_subscriber(name: String, env_filter: String) -> impl Subscriber + Sen
EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new(env_filter));
let formatting_layer = BunyanFormattingLayer::new(
name,
// Output the formatted spans to stdout.
std::io::stdout,
// Output the formatted spans to stderr.
std::io::stderr,
);
Registry::default()
.with(env_filter)
Expand Down
1 change: 0 additions & 1 deletion xcmd-fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ edition = "2021"

[dependencies]
actix-web = { version = "4.4", features = ["rustls"] }
actix-cors = "0.6"
rust-embed = "6.8"
rustls = "0.21"
serde = { version = "1.0", features = ["derive"] }
Expand Down
28 changes: 10 additions & 18 deletions xcmd-fs/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use actix_cors::Cors;
use actix_web::body::to_bytes;
use actix_web::{get, http, post, web, App, HttpResponse, HttpServer};
use actix_web::{get, post, web, App, HttpResponse, HttpServer};
use rust_embed::RustEmbed;
use serde::Deserialize;
use std::error::Error;
Expand All @@ -14,7 +13,7 @@ use tracing::trace;
use tracing_actix_web::TracingLogger;
use urlencoding::encode;
use xcmd_base::{
init_telemetry, stop_server_when_parent_process_exits, FileInfo, ListRequest, ListResponse,
get_port, init_telemetry, post_startup, FileInfo, ListRequest, ListResponse, Middleware,
Request, Response,
};

Expand Down Expand Up @@ -99,32 +98,25 @@ async fn icon(
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
async fn main() -> Result<(), Box<dyn Error>> {
init_telemetry("xcmd_fs");
let port = get_port()?;

let server = HttpServer::new(|| {
let cors = Cors::default()
.allowed_origin("null")
.allowed_origin("tauri://localhost")
.allowed_origin("https://tauri.localhost")
.allowed_origin("http://tauri.localhost")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE)
.max_age(3600);

App::new()
.wrap(Middleware::cors())
.wrap(Middleware::token_auth())
.wrap(TracingLogger::default())
.wrap(cors)
.service(icon)
.service(enact)
})
.bind(("127.0.0.1", 8080))?
.bind(("127.0.0.1", port))?
.run();

stop_server_when_parent_process_exits(&server);
post_startup(&server, port);

server.await
server.await?;
Ok(())
}

fn list_files(request: ListRequest) -> Result<ListResponse, Box<dyn Error>> {
Expand Down
1 change: 0 additions & 1 deletion xcmd-s3/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ edition = "2021"

[dependencies]
actix-web = { version = "4.4", features = ["rustls"] }
actix-cors = "0.6"
rust-embed = "6.8"
rustls = "0.21"
serde = { version = "1.0", features = ["derive"] }
Expand Down
28 changes: 10 additions & 18 deletions xcmd-s3/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use actix_cors::Cors;
use actix_web::{body::to_bytes, get, http, post, web, App, HttpResponse, HttpServer};
use actix_web::{body::to_bytes, get, post, web, App, HttpResponse, HttpServer};
use aws_sdk_s3::{config::Credentials, types::EncodingType};
use aws_types::region::Region;
use rust_embed::RustEmbed;
Expand All @@ -14,7 +13,7 @@ use std::{
use tracing::trace;
use tracing_actix_web::TracingLogger;
use xcmd_base::{
init_telemetry, stop_server_when_parent_process_exits, FileInfo, ListRequest, ListResponse,
get_port, init_telemetry, post_startup, FileInfo, ListRequest, ListResponse, Middleware,
Request, Response,
};

Expand Down Expand Up @@ -49,32 +48,25 @@ async fn icon(name: web::Path<String>) -> Result<HttpResponse, Box<dyn Error>> {
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
async fn main() -> Result<(), Box<dyn Error>> {
init_telemetry("xcmd_s3");
let port = get_port()?;

let server = HttpServer::new(|| {
let cors = Cors::default()
.allowed_origin("null")
.allowed_origin("tauri://localhost")
.allowed_origin("https://tauri.localhost")
.allowed_origin("http://tauri.localhost")
.allowed_methods(vec!["GET", "POST"])
.allowed_headers(vec![http::header::AUTHORIZATION, http::header::ACCEPT])
.allowed_header(http::header::CONTENT_TYPE)
.max_age(3600);

App::new()
.wrap(Middleware::cors())
.wrap(Middleware::token_auth())
.wrap(TracingLogger::default())
.wrap(cors)
.service(icon)
.service(enact)
})
.bind(("127.0.0.1", 8081))?
.bind(("127.0.0.1", port))?
.run();

stop_server_when_parent_process_exits(&server);
post_startup(&server, port);

server.await
server.await?;
Ok(())
}

async fn list_files(request: ListRequest) -> Result<ListResponse, Box<dyn Error>> {
Expand Down
3 changes: 2 additions & 1 deletion xcmd-ssh/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ edition = "2021"

[dependencies]
actix-web = { version = "4.4", features = ["rustls"] }
actix-cors = "0.6"
env_logger = "0.10"
log = "0.4"
rust-embed = "6.8"
Expand All @@ -14,4 +13,6 @@ ssh2 = "0.9"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde_derive = "1.0"
tracing-actix-web = "0.7"
tracing = "0.1"
xcmd-base = { path = "../xcmd-base" }
Loading

0 comments on commit 78d805d

Please sign in to comment.