Skip to content

Commit

Permalink
enha: validate through header
Browse files Browse the repository at this point in the history
  • Loading branch information
carneiro-cw committed Dec 20, 2024
1 parent 113fc1d commit 62a353e
Show file tree
Hide file tree
Showing 4 changed files with 78 additions and 42 deletions.
37 changes: 37 additions & 0 deletions src/eth/rpc/rpc_http_middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use reqwest::header::HeaderMap;
use reqwest::header::HeaderValue;
use tower::Service;

use crate::eth::primitives::StratusError;
use crate::eth::rpc::RpcClientApp;
use crate::ext::not;

Expand All @@ -36,12 +37,48 @@ where

fn call(&mut self, mut request: HttpRequest<HttpBody>) -> Self::Future {
let client_app = parse_client_app(request.headers(), request.uri());
let authentication = parse_admin_password(request.headers());
request.extensions_mut().insert(client_app);
request.extensions_mut().insert(authentication);

Box::pin(self.service.call(request).map_err(Into::into))
}
}

#[derive(Debug, Clone)]
pub enum Authentication {
Admin,
None,
}

impl Authentication {
pub fn auth_admin(&self) -> Result<(), StratusError> {
if matches!(self, Authentication::Admin) {
return Err(StratusError::InvalidPassword);
}
Ok(())
}
}

/// Checks if the provided admin password is correct
fn parse_admin_password(headers: &HeaderMap<HeaderValue>) -> Authentication {
let Ok(real_pass) = std::env::var("ADMIN_PASSWORD") else {
return Authentication::Admin;
};
let Some(value) = headers.get("Authorization") else {
return Authentication::None;
};
let Ok(value) = value.to_str() else { return Authentication::None };
let Some(password) = value.strip_prefix("Password ") else {
return Authentication::None;
};
if real_pass == password {
Authentication::Admin
} else {
Authentication::None
}
}

/// Extracts the client application name from the `app` query parameter.
fn parse_client_app(headers: &HeaderMap<HeaderValue>, uri: &Uri) -> RpcClientApp {
fn try_query_params(uri: &Uri) -> Option<RpcClientApp> {
Expand Down
3 changes: 3 additions & 0 deletions src/eth/rpc/rpc_middleware.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ impl<'a> RpcServiceT<'a> for RpcMiddleware {
_ => None,
};

let is_admin = request.extensions.is_admin();

let client = if let Some(tx_client) = tx.as_ref().and_then(|tx| tx.client.as_ref()) {
let val = tx_client.clone();
request.extensions_mut().insert(val);
Expand Down Expand Up @@ -115,6 +117,7 @@ impl<'a> RpcServiceT<'a> for RpcMiddleware {
rpc_tx_function = %tx.as_ref().map(|tx|tx.function).or_empty(),
rpc_tx_from = %tx.as_ref().and_then(|tx|tx.from).or_empty(),
rpc_tx_to = %tx.as_ref().and_then(|tx|tx.to).or_empty(),
is_admin = %is_admin,
"rpc request"
);

Expand Down
15 changes: 15 additions & 0 deletions src/eth/rpc/rpc_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use jsonrpsee::Extensions;
use rlp::Decodable;
use tracing::Span;

use super::rpc_http_middleware::Authentication;
use crate::eth::primitives::StratusError;
use crate::eth::rpc::rpc_client_app::RpcClientApp;
use crate::ext::type_basename;
Expand All @@ -15,6 +16,12 @@ pub trait RpcExtensionsExt {
/// Returns the client performing the JSON-RPC request.
fn rpc_client(&self) -> &RpcClientApp;

/// Returns current Authentication.
fn authentication(&self) -> &Authentication;

/// Returns wheather admin authentication suceeded.
fn is_admin(&self) -> bool;

/// Enters RpcMiddleware request span if present.
fn enter_middleware_span(&self) -> Option<EnteredWrap<'_>>;
}
Expand All @@ -24,6 +31,14 @@ impl RpcExtensionsExt for Extensions {
self.get::<RpcClientApp>().unwrap_or(&RpcClientApp::Unknown)
}

fn authentication(&self) -> &Authentication {
self.get::<Authentication>().unwrap_or(&Authentication::None)
}

fn is_admin(&self) -> bool {
matches!(self.authentication(), Authentication::Admin)
}

fn enter_middleware_span(&self) -> Option<EnteredWrap<'_>> {
self.get::<Span>().map(|s| s.enter()).map(EnteredWrap::new)
}
Expand Down
65 changes: 23 additions & 42 deletions src/eth/rpc/rpc_server.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
//! RPC server for HTTP and WS.
use std::collections::HashMap;
use std::env;
use std::ops::Deref;
use std::str::FromStr;
use std::sync::Arc;
Expand Down Expand Up @@ -257,13 +256,6 @@ where
Ok(())
}

fn validate_password(pass: &str) -> Result<(), StratusError> {
if env::var_os("ADMIN_PASSWORD").is_some_and(|real_pass| real_pass != pass) {
return Err(StratusError::InvalidPassword);
}
Ok(())
}

// -----------------------------------------------------------------------------
// Debug
// -----------------------------------------------------------------------------
Expand Down Expand Up @@ -328,9 +320,8 @@ fn stratus_reset(_: Params<'_>, ctx: Arc<RpcContext>, _: Extensions) -> Result<J

static MODE_CHANGE_SEMAPHORE: Lazy<Semaphore> = Lazy::new(|| Semaphore::new(1));

async fn stratus_change_to_leader(params: Params<'_>, ctx: Arc<RpcContext>, ext: Extensions) -> Result<JsonValue, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
async fn stratus_change_to_leader(_: Params<'_>, ctx: Arc<RpcContext>, ext: Extensions) -> Result<JsonValue, StratusError> {
ext.authentication().auth_admin()?;
let permit = MODE_CHANGE_SEMAPHORE.try_acquire();
let _permit: SemaphorePermit = match permit {
Ok(permit) => permit,
Expand Down Expand Up @@ -380,8 +371,7 @@ async fn stratus_change_to_leader(params: Params<'_>, ctx: Arc<RpcContext>, ext:
}

async fn stratus_change_to_follower(params: Params<'_>, ctx: Arc<RpcContext>, ext: Extensions) -> Result<JsonValue, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
ext.authentication().auth_admin()?;
let permit = MODE_CHANGE_SEMAPHORE.try_acquire();
let _permit: SemaphorePermit = match permit {
Ok(permit) => permit,
Expand Down Expand Up @@ -435,13 +425,12 @@ async fn stratus_change_to_follower(params: Params<'_>, ctx: Arc<RpcContext>, ex
Ok(json!(true))
}

async fn stratus_init_importer(params: Params<'_>, ctx: Arc<RpcContext>, _: Extensions) -> Result<JsonValue, StratusError> {
async fn stratus_init_importer(params: Params<'_>, ctx: Arc<RpcContext>, ext: Extensions) -> Result<JsonValue, StratusError> {
ext.authentication().auth_admin()?;
let (params, external_rpc) = next_rpc_param::<String>(params.sequence())?;
let (params, external_rpc_ws) = next_rpc_param::<String>(params)?;
let (params, raw_external_rpc_timeout) = next_rpc_param::<String>(params)?;
let (params, raw_sync_interval) = next_rpc_param::<String>(params)?;
let (_, password) = next_rpc_param_or_default::<&str>(params)?;
validate_password(password)?;
let (_, raw_sync_interval) = next_rpc_param::<String>(params)?;

let external_rpc_timeout = parse_duration(&raw_external_rpc_timeout).map_err(|e| {
tracing::error!(reason = ?e, "failed to parse external_rpc_timeout");
Expand All @@ -463,9 +452,8 @@ async fn stratus_init_importer(params: Params<'_>, ctx: Arc<RpcContext>, _: Exte
importer_config.init_follower_importer(ctx).await
}

fn stratus_shutdown_importer(params: Params<'_>, ctx: &RpcContext, _: &Extensions) -> Result<JsonValue, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_shutdown_importer(_: Params<'_>, ctx: &RpcContext, ext: &Extensions) -> Result<JsonValue, StratusError> {
ext.authentication().auth_admin()?;
if GlobalState::get_node_mode() != NodeMode::Follower {
tracing::error!("node is currently not a follower");
return Err(StratusError::StratusNotFollower);
Expand All @@ -484,10 +472,9 @@ fn stratus_shutdown_importer(params: Params<'_>, ctx: &RpcContext, _: &Extension
Ok(json!(true))
}

async fn stratus_change_miner_mode(params: Params<'_>, ctx: Arc<RpcContext>, _: Extensions) -> Result<JsonValue, StratusError> {
let (params, mode_str) = next_rpc_param::<String>(params.sequence())?;
let (_, password) = next_rpc_param_or_default::<&str>(params)?;
validate_password(password)?;
async fn stratus_change_miner_mode(params: Params<'_>, ctx: Arc<RpcContext>, ext: Extensions) -> Result<JsonValue, StratusError> {
ext.authentication().auth_admin()?;
let (_, mode_str) = next_rpc_param::<String>(params.sequence())?;

let mode = MinerMode::from_str(&mode_str).map_err(|e| {
tracing::error!(reason = ?e, "failed to parse miner mode");
Expand Down Expand Up @@ -549,44 +536,38 @@ async fn change_miner_mode(new_mode: MinerMode, ctx: &RpcContext) -> Result<Json
Ok(json!(true))
}

fn stratus_enable_unknown_clients(params: Params<'_>, _: &RpcContext, _: &Extensions) -> Result<bool, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_enable_unknown_clients(_: Params<'_>, _: &RpcContext, ext: &Extensions) -> Result<bool, StratusError> {
ext.authentication().auth_admin()?;
GlobalState::set_unknown_client_enabled(true);
Ok(GlobalState::is_unknown_client_enabled())
}

fn stratus_disable_unknown_clients(params: Params<'_>, _: &RpcContext, _: &Extensions) -> Result<bool, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_disable_unknown_clients(_: Params<'_>, _: &RpcContext, ext: &Extensions) -> Result<bool, StratusError> {
ext.authentication().auth_admin()?;
GlobalState::set_unknown_client_enabled(false);
Ok(GlobalState::is_unknown_client_enabled())
}

fn stratus_enable_transactions(params: Params<'_>, _: &RpcContext, _: &Extensions) -> Result<bool, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_enable_transactions(_: Params<'_>, _: &RpcContext, ext: &Extensions) -> Result<bool, StratusError> {
ext.authentication().auth_admin()?;
GlobalState::set_transactions_enabled(true);
Ok(GlobalState::is_transactions_enabled())
}

fn stratus_disable_transactions(params: Params<'_>, _: &RpcContext, _: &Extensions) -> Result<bool, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_disable_transactions(_: Params<'_>, _: &RpcContext, ext: &Extensions) -> Result<bool, StratusError> {
ext.authentication().auth_admin()?;
GlobalState::set_transactions_enabled(false);
Ok(GlobalState::is_transactions_enabled())
}

fn stratus_enable_miner(params: Params<'_>, ctx: &RpcContext, _: &Extensions) -> Result<bool, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_enable_miner(_: Params<'_>, ctx: &RpcContext, ext: &Extensions) -> Result<bool, StratusError> {
ext.authentication().auth_admin()?;
ctx.miner.unpause();
Ok(true)
}

fn stratus_disable_miner(params: Params<'_>, ctx: &RpcContext, _: &Extensions) -> Result<bool, StratusError> {
let (_, password) = next_rpc_param_or_default::<&str>(params.sequence())?;
validate_password(password)?;
fn stratus_disable_miner(_: Params<'_>, ctx: &RpcContext, ext: &Extensions) -> Result<bool, StratusError> {
ext.authentication().auth_admin()?;
ctx.miner.pause();
Ok(false)
}
Expand Down

0 comments on commit 62a353e

Please sign in to comment.