changes from review. will test now

This commit is contained in:
yenicelik 2023-02-10 17:48:51 +00:00
parent 7877958ab3
commit b1f18460d1
8 changed files with 93 additions and 64 deletions

View File

@ -1,3 +1,3 @@
sea-orm-cli migrate up # sea-orm-cli migrate up
# sea-orm-cli generate entity -u mysql://root:dev_web3_proxy@127.0.0.1:13306/dev_web3_proxy -o entities/src --with-serde both
# sea-orm-cli generate entity -t <table_name> # sea-orm-cli generate entity -t <table_name>

View File

@ -8,8 +8,10 @@ use axum::{
TypedHeader, TypedHeader,
}; };
use axum::response::{IntoResponse, Response}; use axum::response::{IntoResponse, Response};
use entities::{admin, user, user_tier}; use entities::{admin, login, user, user_tier};
use ethers::prelude::Address; use ethers::prelude::Address;
use ethers::types::Bytes;
use ethers::utils::keccak256;
use hashbrown::HashMap; use hashbrown::HashMap;
use http::StatusCode; use http::StatusCode;
use migration::sea_orm::{self, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter}; use migration::sea_orm::{self, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
@ -32,29 +34,13 @@ pub async fn query_admin_modify_usertier<'a>(
// Quickly return if any of the input tokens are bad // Quickly return if any of the input tokens are bad
let user_address: Vec<u8> = params let user_address: Vec<u8> = params
.get("user_address") .get("user_address")
.ok_or_else(|| .ok_or_else(|| FrontendErrorResponse::BadRequest("Unable to find user_address key in request".to_string()))?
FrontendErrorResponse::StatusCode(
StatusCode::BAD_REQUEST,
"Unable to find user_address key in request".to_string(),
None,
)
)?
.parse::<Address>() .parse::<Address>()
.map_err(|err| { .map_err(|_| FrontendErrorResponse::BadRequest("Unable to parse user_address as an Address".to_string()))?
FrontendErrorResponse::StatusCode(
StatusCode::BAD_REQUEST,
"Unable to parse user_address as an Address".to_string(),
Some(err.into()),
)
})?
.to_fixed_bytes().into(); .to_fixed_bytes().into();
let user_tier_title = params let user_tier_title = params
.get("user_tier_title") .get("user_tier_title")
.ok_or_else(|| FrontendErrorResponse::StatusCode( .ok_or_else(||FrontendErrorResponse::BadRequest("Unable to get the user_tier_title key from the request".to_string()))?;
StatusCode::BAD_REQUEST,
"Unable to get the user_tier_title key from the request".to_string(),
None,
))?;
// Prepare output body // Prepare output body
let mut response_body = HashMap::new(); let mut response_body = HashMap::new();
@ -78,22 +64,18 @@ pub async fn query_admin_modify_usertier<'a>(
// Check if the caller is an admin (i.e. if he is in an admin table) // Check if the caller is an admin (i.e. if he is in an admin table)
let admin: admin::Model = admin::Entity::find() let admin: admin::Model = admin::Entity::find()
.filter(admin::Column::UserId.eq(caller_id)) .filter(admin::Column::UserId.eq(caller_id))
.one(&db_conn) .one(db_replica.conn())
.await? .await?
.ok_or(AccessDenied.into())?; .ok_or(AccessDenied)?;
// If we are here, that means an admin was found, and we can safely proceed // If we are here, that means an admin was found, and we can safely proceed
// Fetch the admin, and the user // Fetch the admin, and the user
let user: user::Model = user::Entity::find() let user: user::Model = user::Entity::find()
.filter(user::Column::Address.eq(user_address)) .filter(user::Column::Address.eq(user_address))
.one(&db_conn) .one(db_replica.conn())
.await? .await?
.ok_or(FrontendErrorResponse::StatusCode( .ok_or(FrontendErrorResponse::BadRequest("No user with this id found".to_string()))?;
StatusCode::BAD_REQUEST,
"No user with this id found".to_string(),
None,
))?;
// Return early if the target user_tier_id is the same as the original user_tier_id // Return early if the target user_tier_id is the same as the original user_tier_id
response_body.insert( response_body.insert(
"user_tier_title", "user_tier_title",
@ -101,20 +83,16 @@ pub async fn query_admin_modify_usertier<'a>(
); );
// Now we can modify the user's tier // Now we can modify the user's tier
let new_user_tier: user_tier::Model = !user_tier::Entity::find() let new_user_tier: user_tier::Model = user_tier::Entity::find()
.filter(user_tier::Column::Title.eq(user_tier_title.clone())) .filter(user_tier::Column::Title.eq(user_tier_title.clone()))
.one(&db_conn) .one(db_replica.conn())
.await? .await?
.ok_or(|| FrontendErrorResponse::StatusCode( .ok_or(FrontendErrorResponse::BadRequest("User Tier name was not found".to_string()))?;
StatusCode::BAD_REQUEST,
"User Tier name was not found".to_string(),
None,
))?;
if user.user_tier_id == new_user_tier.id { if user.user_tier_id == new_user_tier.id {
info!("user already has that tier"); info!("user already has that tier");
} else { } else {
let mut user = user.into_active_model(); let mut user = user.clone().into_active_model();
user.user_tier_id = sea_orm::Set(new_user_tier.id); user.user_tier_id = sea_orm::Set(new_user_tier.id);
@ -123,11 +101,35 @@ pub async fn query_admin_modify_usertier<'a>(
info!("user's tier changed"); info!("user's tier changed");
} }
// Finally, remove the user from redis // Query the login table, and get all bearer tokens by this user
// TODO: Also remove the user from the redis let bearer_tokens = login::Entity::find()
// redis_conn.zrem(); .filter(login::Column::UserId.eq(user.id))
// redis_conn.get::<_, u64>(&user.) // TODO: Where do i find the bearer token ... .all(db_replica.conn())
.await?;
// TODO: Remove from Redis
// Remove multiple items simultaneously, but this should be quick let's not prematurely optimize
let recent_user_id_key = format!("recent_users:id:{}", app.config.chain_id);
let salt = app
.config
.public_recent_ips_salt
.as_ref()
.expect("public_recent_ips_salt must exist in here");
// TODO: How do I remove the redis items (?)
for bearer_token in bearer_tokens {
let salted_user_id = format!("{}:{}", salt, bearer_token.user_id);
let hashed_user_id = Bytes::from(keccak256(salted_user_id.as_bytes()));
redis_conn
.zrem(&recent_user_id_key, hashed_user_id.to_string())
.await?;
}
// Now delete these tokens ...
login::Entity::delete_many()
.filter(login::Column::UserId.eq(user.id))
.exec(&db_conn)
.await?;
Ok(Json(&response_body).into_response()) Ok(Json(&response_body).into_response())

View File

@ -1,7 +1,9 @@
use anyhow::Context; use anyhow::Context;
use argh::FromArgs; use argh::FromArgs;
use entities::{admin, login, user}; use entities::{admin, login, user};
use ethers::types::Address; use ethers::types::{Address, Bytes};
use ethers::utils::keccak256;
use http::StatusCode;
use log::{debug, info}; use log::{debug, info};
use migration::sea_orm::{ use migration::sea_orm::{
self, ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, IntoActiveModel, self, ActiveModelTrait, ColumnTrait, DatabaseConnection, EntityTrait, ModelTrait, IntoActiveModel,
@ -34,17 +36,19 @@ impl ChangeUserAdminStatusSubCommand {
.filter(user::Column::Address.eq(address.clone())) .filter(user::Column::Address.eq(address.clone()))
.one(db_conn) .one(db_conn)
.await? .await?
.context("No user found with that address")?; .context(format!("No user with this id found {:?}", address))?;
// Check if there is a record in the database
let mut admin = admin::Entity::find()
.filter(admin::Column::UserId.eq(address))
.all(db_conn)
.await?;
debug!("user: {:#?}", user); debug!("user: {:#?}", user);
match admin.pop() { // Check if there is a record in the database
match admin::Entity::find()
.filter(admin::Column::UserId.eq(address))
.one(db_conn)
.await? {
Some(old_admin) if !should_be_admin => {
// User is already an admin, but shouldn't be
old_admin.delete(db_conn).await?;
}
None if should_be_admin => { None if should_be_admin => {
// User is not an admin yet, but should be // User is not an admin yet, but should be
let new_admin = admin::ActiveModel { let new_admin = admin::ActiveModel {
@ -52,19 +56,42 @@ impl ChangeUserAdminStatusSubCommand {
..Default::default() ..Default::default()
}; };
new_admin.insert(db_conn).await?; new_admin.insert(db_conn).await?;
}, }
Some(old_admin) if !should_be_admin => { _ => {
// User is already an admin, but shouldn't be // Do nothing in this case
old_admin.delete(db_conn).await?; debug!("no change needed for: {:#?}", user);
}, // Early return
_ => {} return Ok(());
}
} }
// Get the bearer tokens of this user and delete them ...
let bearer_tokens = login::Entity::find()
.filter(login::Column::UserId.eq(user.id))
.all(db_conn)
.await?;
// // TODO: Remove from Redis
// // Remove multiple items simultaneously, but this should be quick let's not prematurely optimize
// let recent_user_id_key = format!("recent_users:id:{}", app.config.chain_id);
// let salt = app
// .config
// .public_recent_ips_salt
// .as_ref()
// .expect("public_recent_ips_salt must exist in here");
//
// // TODO: Also clear redis ...
// let salted_user_id = format!("{}:{}", salt, bearer_token.user_id);
// let hashed_user_id = Bytes::from(keccak256(salted_user_id.as_bytes()));
// redis_conn
// .zrem(&recent_user_id_key, hashed_user_id.to_string())
// .await?;
// Remove any user logins from the database (incl. bearer tokens) // Remove any user logins from the database (incl. bearer tokens)
let delete_result = login::Entity::delete_many() let delete_result = login::Entity::delete_many()
.filter(login::Column::UserId.eq(user.id)) .filter(login::Column::UserId.eq(user.id))
.exec(db_conn) .exec(db_conn)
.await; .await?;
debug!("cleared modified logins: {:?}", delete_result); debug!("cleared modified logins: {:?}", delete_result);

View File

@ -309,7 +309,7 @@ fn main() -> anyhow::Result<()> {
SubCommand::ChangeUserTierByAddress(x) => { SubCommand::ChangeUserTierByAddress(x) => {
let db_url = cli_config let db_url = cli_config
.db_url .db_url
.expect("'--config' (with a db) or '--db-url' is required to run proxyd"); .expect("'--config' (with a db) or '--db-url' is required to run change_user_admin_status");
let db_conn = get_db(db_url, 1, 1).await?; let db_conn = get_db(db_url, 1, 1).await?;

View File

@ -17,7 +17,7 @@ use axum::{
response::IntoResponse, response::IntoResponse,
Extension, Json, TypedHeader, Extension, Json, TypedHeader,
}; };
use axum_client_ip::ClientIp; use axum_client_ip::InsecureClientIp;
use axum_macros::debug_handler; use axum_macros::debug_handler;
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};
use entities::sea_orm_active_enums::{LogLevel, Role}; use entities::sea_orm_active_enums::{LogLevel, Role};
@ -67,7 +67,7 @@ pub async fn admin_change_user_roles(
#[debug_handler] #[debug_handler]
pub async fn admin_login_get( pub async fn admin_login_get(
Extension(app): Extension<Arc<Web3ProxyApp>>, Extension(app): Extension<Arc<Web3ProxyApp>>,
ClientIp(ip): ClientIp, InsecureClientIp(ip): InsecureClientIp,
Path(mut params): Path<HashMap<String, String>>, Path(mut params): Path<HashMap<String, String>>,
) -> FrontendResult { ) -> FrontendResult {
// First check if the login is authorized // First check if the login is authorized
@ -229,7 +229,7 @@ pub async fn admin_login_get(
#[debug_handler] #[debug_handler]
pub async fn admin_login_post( pub async fn admin_login_post(
Extension(app): Extension<Arc<Web3ProxyApp>>, Extension(app): Extension<Arc<Web3ProxyApp>>,
ClientIp(ip): ClientIp, InsecureClientIp(ip): InsecureClientIp,
Query(query): Query<PostLoginQuery>, Query(query): Query<PostLoginQuery>,
Json(payload): Json<PostLogin>, Json(payload): Json<PostLogin>,
) -> FrontendResult { ) -> FrontendResult {

View File

@ -172,7 +172,7 @@ pub async fn serve(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()
.route("/admin/modify_role", get(admin::admin_change_user_roles)) .route("/admin/modify_role", get(admin::admin_change_user_roles))
.route("/admin/imitate-login/:user_address", get(admin::admin_login_get)) .route("/admin/imitate-login/:user_address", get(admin::admin_login_get))
.route( .route(
"/user/imitate-login/:user_address/:message_eip", "/admin/imitate-login/:user_address/:message_eip",
get(admin::admin_login_get), get(admin::admin_login_get),
) )
.route("/admin/imitate-login", post(admin::admin_login_post)) .route("/admin/imitate-login", post(admin::admin_login_post))