diff --git a/web3_proxy/src/frontend/authorization.rs b/web3_proxy/src/frontend/authorization.rs index 2a7c0e9c..a2dd3ac7 100644 --- a/web3_proxy/src/frontend/authorization.rs +++ b/web3_proxy/src/frontend/authorization.rs @@ -168,10 +168,10 @@ impl AuthorizedKey { origin: Option, referer: Option, user_agent: Option, - user_data: UserKeyData, + user_key_data: UserKeyData, ) -> anyhow::Result { // check ip - match &user_data.allowed_ips { + match &user_key_data.allowed_ips { None => {} Some(allowed_ips) => { if !allowed_ips.iter().any(|x| x.contains(&ip)) { @@ -183,7 +183,7 @@ impl AuthorizedKey { // check origin // TODO: do this with the Origin type instead of a String? let origin = origin.map(|x| x.to_string()); - match (&origin, &user_data.allowed_origins) { + match (&origin, &user_key_data.allowed_origins) { (None, None) => {} (Some(_), None) => {} (None, Some(_)) => return Err(anyhow::anyhow!("Origin required")), @@ -197,7 +197,7 @@ impl AuthorizedKey { } // check referer - match (referer, &user_data.allowed_referers) { + match (referer, &user_key_data.allowed_referers) { (None, None) => {} (Some(_), None) => {} (None, Some(_)) => return Err(anyhow::anyhow!("Referer required")), @@ -209,7 +209,7 @@ impl AuthorizedKey { } // check user_agent - match (user_agent, &user_data.allowed_user_agents) { + match (user_agent, &user_key_data.allowed_user_agents) { (None, None) => {} (Some(_), None) => {} (None, Some(_)) => return Err(anyhow::anyhow!("User agent required")), @@ -223,8 +223,8 @@ impl AuthorizedKey { Ok(Self { ip, origin, - user_key_id: user_data.user_key_id, - log_revert_chance: user_data.log_revert_chance, + user_key_id: user_key_data.user_key_id, + log_revert_chance: user_key_data.log_revert_chance, }) } } @@ -268,13 +268,10 @@ pub async fn login_is_authorized( Ok((AuthorizedRequest::Ip(ip), semaphore)) } +// TODO: where should we use this? pub async fn bearer_is_authorized( app: &Web3ProxyApp, bearer: Bearer, - ip: IpAddr, - origin: Option, - referer: Option, - user_agent: Option, ) -> Result<(AuthorizedRequest, Option), FrontendErrorResponse> { let mut redis_conn = app.redis_conn().await.context("Getting redis connection")?; @@ -290,22 +287,13 @@ pub async fn bearer_is_authorized( let db_conn = app.db_conn().context("Getting database connection")?; // turn user key id into a user key - let user_key_data = user::Entity::find_by_id(user_id) + let user_data = user::Entity::find_by_id(user_id) .one(&db_conn) .await .context("fetching user by id")? .context("unknown user id")?; todo!("rewrite this. key_is_authorized is wrong. we should check user ids instead") - // key_is_authorized( - // app, - // user_key_data.api_key.into(), - // ip, - // origin, - // referer, - // user_agent, - // ) - // .await } pub async fn ip_is_authorized( diff --git a/web3_proxy/src/frontend/errors.rs b/web3_proxy/src/frontend/errors.rs index 94023e4f..9d5316dd 100644 --- a/web3_proxy/src/frontend/errors.rs +++ b/web3_proxy/src/frontend/errors.rs @@ -13,9 +13,10 @@ use std::{error::Error, net::IpAddr}; use tokio::time::Instant; use tracing::warn; -// TODO: take "IntoResult" instead? +// TODO: take "IntoResponse" instead of Response? pub type FrontendResult = Result; +// TODO: #[derive(From)] pub enum FrontendErrorResponse { Anyhow(anyhow::Error), @@ -25,6 +26,8 @@ pub enum FrontendErrorResponse { Database(DbErr), RateLimitedUser(UserKeyData, Option), RateLimitedIp(IpAddr, Option), + /// simple way to return an error message to the user and an anyhow to our logs + StatusCode(StatusCode, String, anyhow::Error), UnknownKey, NotFound, } @@ -72,6 +75,17 @@ impl IntoResponse for FrontendErrorResponse { debug_assert_ne!(r.status(), StatusCode::OK); return r; } + Self::StatusCode(status_code, err_msg, err) => { + warn!(?status_code, ?err_msg, ?err); + ( + status_code, + JsonRpcForwardedResponse::from_str( + &err_msg, + Some(status_code.as_u16().into()), + None, + ), + ) + } Self::Database(err) => { warn!(?err, "database"); ( diff --git a/web3_proxy/src/frontend/rpc_proxy_http.rs b/web3_proxy/src/frontend/rpc_proxy_http.rs index 05775dc6..427137bc 100644 --- a/web3_proxy/src/frontend/rpc_proxy_http.rs +++ b/web3_proxy/src/frontend/rpc_proxy_http.rs @@ -1,11 +1,10 @@ //! Take a user's HTTP JSON-RPC requests and either respond from local data or proxy the request to a backend rpc server. -use super::authorization::{bearer_is_authorized, ip_is_authorized, key_is_authorized}; +use super::authorization::{ip_is_authorized, key_is_authorized}; use super::errors::FrontendResult; use crate::{app::Web3ProxyApp, jsonrpc::JsonRpcRequestEnum}; use axum::extract::Path; -use axum::headers::authorization::Bearer; -use axum::headers::{Authorization, Origin, Referer, UserAgent}; +use axum::headers::{Origin, Referer, UserAgent}; use axum::TypedHeader; use axum::{response::IntoResponse, Extension, Json}; use axum_client_ip::ClientIp; @@ -19,29 +18,13 @@ use tracing::{error_span, Instrument}; #[debug_handler] pub async fn proxy_web3_rpc( Extension(app): Extension>, - bearer: Option>>, ClientIp(ip): ClientIp, Json(payload): Json, - origin: Option>, - referer: Option>, - user_agent: Option>, ) -> FrontendResult { - let request_span = error_span!("request", %ip, ?referer, ?user_agent); + let request_span = error_span!("request", %ip); - let (authorized_request, _semaphore) = if let Some(TypedHeader(Authorization(bearer))) = bearer - { - let origin = origin.map(|x| x.0); - let referer = referer.map(|x| x.0); - let user_agent = user_agent.map(|x| x.0); - - bearer_is_authorized(&app, bearer, ip, origin, referer, user_agent) - .instrument(request_span.clone()) - .await? - } else { - ip_is_authorized(&app, ip) - .instrument(request_span.clone()) - .await? - }; + let (authorized_request, _semaphore) = + ip_is_authorized(&app, ip).instrument(request_span).await?; let request_span = error_span!("request", ?authorized_request); diff --git a/web3_proxy/src/frontend/rpc_proxy_ws.rs b/web3_proxy/src/frontend/rpc_proxy_ws.rs index 024b18b1..2977f46c 100644 --- a/web3_proxy/src/frontend/rpc_proxy_ws.rs +++ b/web3_proxy/src/frontend/rpc_proxy_ws.rs @@ -2,11 +2,9 @@ //! //! WebSockets are the preferred method of receiving requests, but not all clients have good support. -use super::authorization::{ - bearer_is_authorized, ip_is_authorized, key_is_authorized, AuthorizedRequest, -}; +use super::authorization::{ip_is_authorized, key_is_authorized, AuthorizedRequest}; use super::errors::FrontendResult; -use axum::headers::{authorization::Bearer, Authorization, Origin, Referer, UserAgent}; +use axum::headers::{Origin, Referer, UserAgent}; use axum::{ extract::ws::{Message, WebSocket, WebSocketUpgrade}, extract::Path, @@ -36,30 +34,15 @@ use crate::{ /// Defaults to rate limiting by IP address, but can also read the Authorization header for a bearer token. #[debug_handler] pub async fn websocket_handler( - bearer: Option>>, Extension(app): Extension>, ClientIp(ip): ClientIp, - origin: Option>, - referer: Option>, - user_agent: Option>, ws_upgrade: Option, ) -> FrontendResult { - let request_span = error_span!("request", %ip, ?referer, ?user_agent); + // TODO: i don't like logging ips. move this to trace level? + let request_span = error_span!("request", %ip); - let (authorized_request, _semaphore) = if let Some(TypedHeader(Authorization(bearer))) = bearer - { - let origin = origin.map(|x| x.0); - let referer = referer.map(|x| x.0); - let user_agent = user_agent.map(|x| x.0); - - bearer_is_authorized(&app, bearer, ip, origin, referer, user_agent) - .instrument(request_span.clone()) - .await? - } else { - ip_is_authorized(&app, ip) - .instrument(request_span.clone()) - .await? - }; + let (authorized_request, _semaphore) = + ip_is_authorized(&app, ip).instrument(request_span).await?; let request_span = error_span!("request", ?authorized_request); diff --git a/web3_proxy/src/frontend/users.rs b/web3_proxy/src/frontend/users.rs index 92bf5f53..de84d826 100644 --- a/web3_proxy/src/frontend/users.rs +++ b/web3_proxy/src/frontend/users.rs @@ -3,6 +3,7 @@ use super::authorization::{login_is_authorized, UserKey}; use super::errors::FrontendResult; use crate::app::Web3ProxyApp; +use crate::frontend::authorization::bearer_is_authorized; use crate::user_queries::{get_aggregate_rpc_stats_from_params, get_detailed_stats}; use anyhow::Context; use axum::{ @@ -323,7 +324,10 @@ pub async fn user_login_post( .await?; if let Err(err) = redis_conn.del::<_, u64>(&login_nonce_key).await { - warn!("Failed to delete login_nonce_key: {}", login_nonce_key); + warn!( + "Failed to delete login_nonce_key {}: {}", + login_nonce_key, err + ); } Ok(response) @@ -402,9 +406,11 @@ pub async fn user_profile_post( /// TODO: this will change as we add better support for secondary users. #[debug_handler] pub async fn user_balance_get( - TypedHeader(Authorization(bearer_token)): TypedHeader>, Extension(app): Extension>, + TypedHeader(Authorization(bearer)): TypedHeader>, ) -> FrontendResult { + let (authorized_request, _semaphore) = bearer_is_authorized(&app, bearer).await?; + todo!("user_balance_get"); } @@ -485,9 +491,6 @@ pub async fn user_stats_detailed_get( Extension(app): Extension>, Query(params): Query>, ) -> FrontendResult { - let db_conn = app.db_conn().context("connecting to db")?; - let redis_conn = app.redis_conn().await.context("connecting to redis")?; - let x = get_detailed_stats(&app, bearer, params).await?; Ok(Json(x).into_response()) diff --git a/web3_proxy/src/user_queries.rs b/web3_proxy/src/user_queries.rs index 748762fe..ce8ec371 100644 --- a/web3_proxy/src/user_queries.rs +++ b/web3_proxy/src/user_queries.rs @@ -423,8 +423,6 @@ pub async fn get_detailed_stats( (condition, q) }; - let q = q.filter(condition); - let q = if query_window_seconds != 0 { /* let query_start_timestamp: u64 = query_start @@ -452,6 +450,8 @@ pub async fn get_detailed_stats( q }; + let q = q.filter(condition); + // log query here. i think sea orm has a useful log level for this // TODO: transform this into a nested hashmap instead of a giant table?