check for bearer token on /rpc
This commit is contained in:
parent
81deb1103c
commit
b254cb7d26
@ -689,6 +689,10 @@ impl Web3ProxyApp {
|
||||
Ok(collected)
|
||||
}
|
||||
|
||||
pub fn db_conn(&self) -> Option<&DatabaseConnection> {
|
||||
self.db_conn.as_ref()
|
||||
}
|
||||
|
||||
pub async fn redis_conn(&self) -> anyhow::Result<redis_rate_limiter::RedisConnection> {
|
||||
match self.redis_pool.as_ref() {
|
||||
None => Err(anyhow::anyhow!("no redis server configured")),
|
||||
|
@ -1,10 +1,11 @@
|
||||
use super::errors::FrontendErrorResponse;
|
||||
use crate::app::{UserKeyData, Web3ProxyApp};
|
||||
use anyhow::Context;
|
||||
use axum::headers::{Origin, Referer, UserAgent};
|
||||
use axum::headers::{authorization::Bearer, Origin, Referer, UserAgent};
|
||||
use deferred_rate_limiter::DeferredRateLimitResult;
|
||||
use entities::user_keys;
|
||||
use ipnet::IpNet;
|
||||
use redis_rate_limiter::redis::AsyncCommands;
|
||||
use redis_rate_limiter::RedisRateLimitResult;
|
||||
use sea_orm::{prelude::Decimal, ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter};
|
||||
use serde::Serialize;
|
||||
@ -143,6 +144,37 @@ pub async fn login_is_authorized(
|
||||
Ok(AuthorizedRequest::Ip(db, ip))
|
||||
}
|
||||
|
||||
pub async fn bearer_is_authorized(
|
||||
app: &Web3ProxyApp,
|
||||
bearer: Bearer,
|
||||
ip: IpAddr,
|
||||
origin: Option<Origin>,
|
||||
referer: Option<Referer>,
|
||||
user_agent: Option<UserAgent>,
|
||||
) -> Result<AuthorizedRequest, FrontendErrorResponse> {
|
||||
let mut redis_conn = app.redis_conn().await.context("Getting redis connection")?;
|
||||
|
||||
// TODO: verify that bearer.token() is a Ulid?
|
||||
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
||||
|
||||
// turn bearer into a user key id
|
||||
let user_key_id: u64 = redis_conn
|
||||
.get(bearer_cache_key)
|
||||
.await
|
||||
.context("unknown bearer token")?;
|
||||
|
||||
let db_conn = app.db_conn().context("Getting database connection")?;
|
||||
|
||||
// turn user key id into a user key
|
||||
let user_key_data = user_keys::Entity::find_by_id(user_key_id)
|
||||
.one(db_conn)
|
||||
.await
|
||||
.context("fetching user key by id")?
|
||||
.context("unknown user id")?;
|
||||
|
||||
key_is_authorized(app, user_key_data.api_key, ip, origin, referer, user_agent).await
|
||||
}
|
||||
|
||||
pub async fn ip_is_authorized(
|
||||
app: &Web3ProxyApp,
|
||||
ip: IpAddr,
|
||||
@ -261,7 +293,7 @@ impl Web3ProxyApp {
|
||||
.try_get_with(user_key, async move {
|
||||
trace!(?user_key, "user_cache miss");
|
||||
|
||||
let db = self.db_conn.as_ref().context("no database")?;
|
||||
let db = self.db_conn().context("Getting database connection")?;
|
||||
|
||||
// TODO: join the user table to this to return the User? we don't always need it
|
||||
match user_keys::Entity::find()
|
||||
|
@ -46,9 +46,12 @@ pub async fn serve(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()
|
||||
// TODO: these should probbably all start with /rpc. then / can be the static site
|
||||
let app = Router::new()
|
||||
// routes should be order most to least common
|
||||
.route("/rpc", post(rpc_proxy_http::public_proxy_web3_rpc))
|
||||
.route("/rpc", post(rpc_proxy_http::proxy_web3_rpc))
|
||||
.route("/rpc", get(rpc_proxy_ws::public_websocket_handler))
|
||||
.route("/rpc/:user_key", post(rpc_proxy_http::user_proxy_web3_rpc))
|
||||
.route(
|
||||
"/rpc/:user_key",
|
||||
post(rpc_proxy_http::proxy_web3_rpc_with_key),
|
||||
)
|
||||
.route("/rpc/:user_key", get(rpc_proxy_ws::user_websocket_handler))
|
||||
.route("/rpc/health", get(http::health))
|
||||
.route("/rpc/status", get(http::status))
|
||||
|
@ -1,8 +1,9 @@
|
||||
use super::authorization::{ip_is_authorized, key_is_authorized};
|
||||
use super::authorization::{bearer_is_authorized, ip_is_authorized, key_is_authorized};
|
||||
use super::errors::FrontendResult;
|
||||
use crate::{app::Web3ProxyApp, jsonrpc::JsonRpcRequestEnum};
|
||||
use axum::extract::Path;
|
||||
use axum::headers::{Origin, Referer, UserAgent};
|
||||
use axum::headers::authorization::Bearer;
|
||||
use axum::headers::{Authorization, Origin, Referer, UserAgent};
|
||||
use axum::TypedHeader;
|
||||
use axum::{response::IntoResponse, Extension, Json};
|
||||
use axum_client_ip::ClientIp;
|
||||
@ -10,18 +11,30 @@ use std::sync::Arc;
|
||||
use tracing::{error_span, Instrument};
|
||||
use uuid::Uuid;
|
||||
|
||||
pub async fn public_proxy_web3_rpc(
|
||||
pub async fn proxy_web3_rpc(
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
bearer: Option<TypedHeader<Authorization<Bearer>>>,
|
||||
ClientIp(ip): ClientIp,
|
||||
Json(payload): Json<JsonRpcRequestEnum>,
|
||||
origin: Option<TypedHeader<Origin>>,
|
||||
referer: Option<TypedHeader<Referer>>,
|
||||
user_agent: Option<TypedHeader<UserAgent>>,
|
||||
) -> FrontendResult {
|
||||
let request_span = error_span!("request", %ip, ?referer, ?user_agent);
|
||||
|
||||
let authorization = ip_is_authorized(&app, ip)
|
||||
.instrument(request_span.clone())
|
||||
.await?;
|
||||
let authorization = 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 request_span = error_span!("request", ?authorization);
|
||||
|
||||
@ -38,7 +51,7 @@ pub async fn public_proxy_web3_rpc(
|
||||
Ok(Json(&response).into_response())
|
||||
}
|
||||
|
||||
pub async fn user_proxy_web3_rpc(
|
||||
pub async fn proxy_web3_rpc_with_key(
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
ClientIp(ip): ClientIp,
|
||||
Json(payload): Json<JsonRpcRequestEnum>,
|
||||
|
@ -175,7 +175,7 @@ pub async fn post_login(
|
||||
|
||||
let bearer_token = Ulid::new();
|
||||
|
||||
let db = app.db_conn.as_ref().unwrap();
|
||||
let db = app.db_conn().context("Getting database connection")?;
|
||||
|
||||
// TODO: limit columns or load whole user?
|
||||
let u = user::Entity::find()
|
||||
@ -263,11 +263,11 @@ pub async fn get_logout(
|
||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
|
||||
) -> FrontendResult {
|
||||
let mut redis_conn = app.redis_conn().await?;
|
||||
|
||||
// TODO: i don't like this. move this to a helper function so it is less fragile
|
||||
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
||||
|
||||
let mut redis_conn = app.redis_conn().await?;
|
||||
|
||||
redis_conn.del(bearer_cache_key).await?;
|
||||
|
||||
// TODO: what should the response be? probably json something
|
||||
@ -310,7 +310,7 @@ pub async fn post_user(
|
||||
}
|
||||
}
|
||||
|
||||
let db = app.db_conn.as_ref().unwrap();
|
||||
let db = app.db_conn().context("Getting database connection")?;
|
||||
|
||||
user.save(db).await?;
|
||||
|
||||
@ -332,16 +332,19 @@ impl ProtectedAction {
|
||||
async fn verify(
|
||||
self,
|
||||
app: &Web3ProxyApp,
|
||||
// TODO: i don't think we want Bearer here. we want user_key and a helper for bearer -> user_key
|
||||
bearer: Bearer,
|
||||
primary_address: &Address,
|
||||
) -> anyhow::Result<user::Model> {
|
||||
// get the attached address from redis for the given auth_token.
|
||||
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
||||
|
||||
let mut redis_conn = app.redis_conn().await?;
|
||||
|
||||
// TODO: is this type correct?
|
||||
let u_id: Option<u64> = redis_conn.get(bearer_cache_key).await?;
|
||||
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
||||
|
||||
let user_key_id: Option<u64> = redis_conn
|
||||
.get(bearer_cache_key)
|
||||
.await
|
||||
.context("fetching bearer cache key from redis")?;
|
||||
|
||||
// TODO: if auth_address == primary_address, allow
|
||||
// TODO: if auth_address != primary_address, only allow if they are a secondary user with the correct role
|
||||
|
Loading…
Reference in New Issue
Block a user