From 1d24955d6f3d78e99e4cf23061ac748ef061c10c Mon Sep 17 00:00:00 2001 From: Bryan Stitt Date: Tue, 25 Oct 2022 04:12:24 +0000 Subject: [PATCH] finish basic ProtectedAction --- web3_proxy/src/config.rs | 2 +- web3_proxy/src/frontend/authorization.rs | 32 +----------------- web3_proxy/src/frontend/users.rs | 42 +++++++++++++++--------- web3_proxy/src/rpcs/connection.rs | 2 +- 4 files changed, 29 insertions(+), 49 deletions(-) diff --git a/web3_proxy/src/config.rs b/web3_proxy/src/config.rs index f986b4c1..50559a4f 100644 --- a/web3_proxy/src/config.rs +++ b/web3_proxy/src/config.rs @@ -121,7 +121,7 @@ pub struct AppConfig { /// the stats page url for a logged in user. if set, must contain "{user_id}" pub redirect_user_url: Option, - /// https://sentry.io + /// Optionally send errors to pub sentry_url: Option, } diff --git a/web3_proxy/src/frontend/authorization.rs b/web3_proxy/src/frontend/authorization.rs index 6a8a2a70..330a8aa2 100644 --- a/web3_proxy/src/frontend/authorization.rs +++ b/web3_proxy/src/frontend/authorization.rs @@ -263,8 +263,6 @@ pub async fn login_is_authorized( app: &Web3ProxyApp, ip: IpAddr, ) -> Result { - // TODO: i think we could write an `impl From` for this - // TODO: move this to an AuthorizedUser extrator let (ip, _semaphore) = match app.rate_limit_login(ip).await? { RateLimitResult::AllowedIp(x, semaphore) => (x, semaphore), RateLimitResult::RateLimitedIp(x, retry_at) => { @@ -277,34 +275,6 @@ pub async fn login_is_authorized( Ok(AuthorizedRequest::Ip(ip, None)) } -// TODO: where should we use this? -pub async fn bearer_is_authorized( - app: &Web3ProxyApp, - bearer: Bearer, -) -> Result<(AuthorizedRequest, Option), 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_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_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") -} - pub async fn ip_is_authorized( app: &Web3ProxyApp, ip: IpAddr, @@ -408,7 +378,7 @@ impl Web3ProxyApp { pub async fn rate_limit_login(&self, ip: IpAddr) -> anyhow::Result { // TODO: dry this up with rate_limit_by_key - // TODO: do we ant semafores here? + // TODO: do we want a semaphore here? if let Some(rate_limiter) = &self.login_rate_limiter { match rate_limiter.throttle_label(&ip.to_string(), None, 1).await { Ok(RedisRateLimitResult::Allowed(_)) => Ok(RateLimitResult::AllowedIp(ip, None)), diff --git a/web3_proxy/src/frontend/users.rs b/web3_proxy/src/frontend/users.rs index 49b7e507..6c6bb4f1 100644 --- a/web3_proxy/src/frontend/users.rs +++ b/web3_proxy/src/frontend/users.rs @@ -3,7 +3,6 @@ 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::{ @@ -358,7 +357,6 @@ pub struct PostUser { primary_address: Address, // TODO: make sure the email address is valid. probably have a "verified" column in the database email: Option, - // TODO: make them sign this JSON? cookie in session id is hard because its on a different domain } /// `POST /user/profile` -- modify the account connected to the bearer token in the `Authentication` header. @@ -409,8 +407,6 @@ pub async fn user_balance_get( Extension(app): Extension>, TypedHeader(Authorization(bearer)): TypedHeader>, ) -> FrontendResult { - let (authorized_request, _semaphore) = bearer_is_authorized(&app, bearer).await?; - todo!("user_balance_get"); } @@ -515,12 +511,7 @@ enum ProtectedAction { impl ProtectedAction { /// Verify that the given bearer token and address are allowed to take the specified action. - 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, - ) -> anyhow::Result { + async fn verify(self, app: &Web3ProxyApp, bearer: Bearer) -> anyhow::Result { // get the attached address from redis for the given auth_token. let mut redis_conn = app.redis_conn().await?; @@ -528,13 +519,32 @@ impl ProtectedAction { let bearer_cache_key = format!("bearer:{}", bearer.token()); // TODO: move this to a helper function - let user_id: Option = redis_conn - .get(bearer_cache_key) + let user_id: u64 = redis_conn + .get::<_, Option>(bearer_cache_key) .await - .context("fetching bearer cache key from redis")?; + .context("fetching bearer cache key from redis")? + .context("unknown bearer token")?; - // 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 - todo!("verify token for the given user"); + let db_conn = app.db_conn().context("Getting database connection")?; + + // turn user key id into a user key + let user_data = user::Entity::find_by_id(user_id) + .one(&db_conn) + .await + .context("fetching user from db by id")? + .context("unknown user id")?; + + match self { + Self::PostUser(primary_address) => { + let user_address = Address::from_slice(&user_data.address); + + if user_address != primary_address { + // TODO: check secondary users + return Err(anyhow::anyhow!("user address mismatch")); + } + } + } + + Ok(user_data) } } diff --git a/web3_proxy/src/rpcs/connection.rs b/web3_proxy/src/rpcs/connection.rs index 9186426f..5761aba0 100644 --- a/web3_proxy/src/rpcs/connection.rs +++ b/web3_proxy/src/rpcs/connection.rs @@ -259,7 +259,7 @@ impl Web3Connection { } /// reconnect to the provider. errors are retried forever with exponential backoff with jitter. - /// We use the "Decorrelated" jitter from https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ + /// We use the "Decorrelated" jitter from /// TODO: maybe it would be better to use "Full Jitter". The "Full Jitter" approach uses less work, but slightly more time. pub async fn retrying_reconnect( self: &Arc,