finish basic ProtectedAction
This commit is contained in:
parent
618bfeb861
commit
1d24955d6f
@ -121,7 +121,7 @@ pub struct AppConfig {
|
|||||||
/// the stats page url for a logged in user. if set, must contain "{user_id}"
|
/// the stats page url for a logged in user. if set, must contain "{user_id}"
|
||||||
pub redirect_user_url: Option<String>,
|
pub redirect_user_url: Option<String>,
|
||||||
|
|
||||||
/// https://sentry.io
|
/// Optionally send errors to <https://sentry.io>
|
||||||
pub sentry_url: Option<String>,
|
pub sentry_url: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,8 +263,6 @@ pub async fn login_is_authorized(
|
|||||||
app: &Web3ProxyApp,
|
app: &Web3ProxyApp,
|
||||||
ip: IpAddr,
|
ip: IpAddr,
|
||||||
) -> Result<AuthorizedRequest, FrontendErrorResponse> {
|
) -> Result<AuthorizedRequest, FrontendErrorResponse> {
|
||||||
// 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? {
|
let (ip, _semaphore) = match app.rate_limit_login(ip).await? {
|
||||||
RateLimitResult::AllowedIp(x, semaphore) => (x, semaphore),
|
RateLimitResult::AllowedIp(x, semaphore) => (x, semaphore),
|
||||||
RateLimitResult::RateLimitedIp(x, retry_at) => {
|
RateLimitResult::RateLimitedIp(x, retry_at) => {
|
||||||
@ -277,34 +275,6 @@ pub async fn login_is_authorized(
|
|||||||
Ok(AuthorizedRequest::Ip(ip, None))
|
Ok(AuthorizedRequest::Ip(ip, None))
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: where should we use this?
|
|
||||||
pub async fn bearer_is_authorized(
|
|
||||||
app: &Web3ProxyApp,
|
|
||||||
bearer: Bearer,
|
|
||||||
) -> Result<(AuthorizedRequest, Option<OwnedSemaphorePermit>), 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(
|
pub async fn ip_is_authorized(
|
||||||
app: &Web3ProxyApp,
|
app: &Web3ProxyApp,
|
||||||
ip: IpAddr,
|
ip: IpAddr,
|
||||||
@ -408,7 +378,7 @@ impl Web3ProxyApp {
|
|||||||
|
|
||||||
pub async fn rate_limit_login(&self, ip: IpAddr) -> anyhow::Result<RateLimitResult> {
|
pub async fn rate_limit_login(&self, ip: IpAddr) -> anyhow::Result<RateLimitResult> {
|
||||||
// TODO: dry this up with rate_limit_by_key
|
// 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 {
|
if let Some(rate_limiter) = &self.login_rate_limiter {
|
||||||
match rate_limiter.throttle_label(&ip.to_string(), None, 1).await {
|
match rate_limiter.throttle_label(&ip.to_string(), None, 1).await {
|
||||||
Ok(RedisRateLimitResult::Allowed(_)) => Ok(RateLimitResult::AllowedIp(ip, None)),
|
Ok(RedisRateLimitResult::Allowed(_)) => Ok(RateLimitResult::AllowedIp(ip, None)),
|
||||||
|
@ -3,7 +3,6 @@
|
|||||||
use super::authorization::{login_is_authorized, UserKey};
|
use super::authorization::{login_is_authorized, UserKey};
|
||||||
use super::errors::FrontendResult;
|
use super::errors::FrontendResult;
|
||||||
use crate::app::Web3ProxyApp;
|
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 crate::user_queries::{get_aggregate_rpc_stats_from_params, get_detailed_stats};
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use axum::{
|
use axum::{
|
||||||
@ -358,7 +357,6 @@ pub struct PostUser {
|
|||||||
primary_address: Address,
|
primary_address: Address,
|
||||||
// TODO: make sure the email address is valid. probably have a "verified" column in the database
|
// TODO: make sure the email address is valid. probably have a "verified" column in the database
|
||||||
email: Option<String>,
|
email: Option<String>,
|
||||||
// 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.
|
/// `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<Arc<Web3ProxyApp>>,
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||||
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
|
TypedHeader(Authorization(bearer)): TypedHeader<Authorization<Bearer>>,
|
||||||
) -> FrontendResult {
|
) -> FrontendResult {
|
||||||
let (authorized_request, _semaphore) = bearer_is_authorized(&app, bearer).await?;
|
|
||||||
|
|
||||||
todo!("user_balance_get");
|
todo!("user_balance_get");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -515,12 +511,7 @@ enum ProtectedAction {
|
|||||||
|
|
||||||
impl ProtectedAction {
|
impl ProtectedAction {
|
||||||
/// Verify that the given bearer token and address are allowed to take the specified action.
|
/// Verify that the given bearer token and address are allowed to take the specified action.
|
||||||
async fn verify(
|
async fn verify(self, app: &Web3ProxyApp, bearer: Bearer) -> anyhow::Result<user::Model> {
|
||||||
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<user::Model> {
|
|
||||||
// get the attached address from redis for the given auth_token.
|
// get the attached address from redis for the given auth_token.
|
||||||
let mut redis_conn = app.redis_conn().await?;
|
let mut redis_conn = app.redis_conn().await?;
|
||||||
|
|
||||||
@ -528,13 +519,32 @@ impl ProtectedAction {
|
|||||||
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
let bearer_cache_key = format!("bearer:{}", bearer.token());
|
||||||
|
|
||||||
// TODO: move this to a helper function
|
// TODO: move this to a helper function
|
||||||
let user_id: Option<u64> = redis_conn
|
let user_id: u64 = redis_conn
|
||||||
.get(bearer_cache_key)
|
.get::<_, Option<u64>>(bearer_cache_key)
|
||||||
.await
|
.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
|
let db_conn = app.db_conn().context("Getting database connection")?;
|
||||||
// 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");
|
// 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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ impl Web3Connection {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// reconnect to the provider. errors are retried forever with exponential backoff with jitter.
|
/// 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 <https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/>
|
||||||
/// TODO: maybe it would be better to use "Full Jitter". The "Full Jitter" approach uses less work, but slightly more time.
|
/// 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(
|
pub async fn retrying_reconnect(
|
||||||
self: &Arc<Self>,
|
self: &Arc<Self>,
|
||||||
|
Loading…
Reference in New Issue
Block a user