2023-02-19 23:27:53 +03:00
|
|
|
use crate::app::Web3ProxyApp;
|
|
|
|
use crate::frontend::errors::FrontendErrorResponse;
|
|
|
|
use crate::user_queries::get_user_id_from_params;
|
|
|
|
use anyhow::Context;
|
|
|
|
use axum::{
|
|
|
|
Json,
|
|
|
|
headers::{authorization::Bearer, Authorization},
|
|
|
|
TypedHeader,
|
|
|
|
};
|
|
|
|
use axum::response::{IntoResponse, Response};
|
2023-02-10 20:48:51 +03:00
|
|
|
use entities::{admin, login, user, user_tier};
|
2023-02-19 23:27:53 +03:00
|
|
|
use ethers::prelude::Address;
|
2023-02-10 20:48:51 +03:00
|
|
|
use ethers::types::Bytes;
|
|
|
|
use ethers::utils::keccak256;
|
2023-02-19 23:27:53 +03:00
|
|
|
use hashbrown::HashMap;
|
|
|
|
use http::StatusCode;
|
2023-01-17 22:12:40 +03:00
|
|
|
use migration::sea_orm::{self, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
|
2023-02-19 23:27:53 +03:00
|
|
|
use log::info;
|
2023-02-19 23:33:33 +03:00
|
|
|
use redis_rate_limiter::redis::AsyncCommands;
|
2023-01-31 17:08:46 +03:00
|
|
|
use crate::frontend::errors::FrontendErrorResponse::AccessDenied;
|
2023-02-19 23:27:53 +03:00
|
|
|
|
2023-02-19 23:34:39 +03:00
|
|
|
// TODO: Add some logic to check if the operating user is an admin
|
|
|
|
// If he is, return true
|
|
|
|
// If he is not, return false
|
|
|
|
// This function is used to give permission to certain users
|
|
|
|
|
2023-02-19 23:27:53 +03:00
|
|
|
|
|
|
|
pub async fn query_admin_modify_usertier<'a>(
|
|
|
|
app: &'a Web3ProxyApp,
|
|
|
|
bearer: Option<TypedHeader<Authorization<Bearer>>>,
|
|
|
|
params: &'a HashMap<String, String>
|
|
|
|
) -> Result<Response, FrontendErrorResponse> {
|
|
|
|
|
|
|
|
// Quickly return if any of the input tokens are bad
|
|
|
|
let user_address: Vec<u8> = params
|
|
|
|
.get("user_address")
|
2023-02-10 20:48:51 +03:00
|
|
|
.ok_or_else(|| FrontendErrorResponse::BadRequest("Unable to find user_address key in request".to_string()))?
|
2023-02-19 23:27:53 +03:00
|
|
|
.parse::<Address>()
|
2023-02-10 20:48:51 +03:00
|
|
|
.map_err(|_| FrontendErrorResponse::BadRequest("Unable to parse user_address as an Address".to_string()))?
|
2023-02-19 23:27:53 +03:00
|
|
|
.to_fixed_bytes().into();
|
|
|
|
let user_tier_title = params
|
|
|
|
.get("user_tier_title")
|
2023-02-10 20:48:51 +03:00
|
|
|
.ok_or_else(||FrontendErrorResponse::BadRequest("Unable to get the user_tier_title key from the request".to_string()))?;
|
2023-02-19 23:27:53 +03:00
|
|
|
|
|
|
|
// Prepare output body
|
|
|
|
let mut response_body = HashMap::new();
|
|
|
|
|
|
|
|
// Establish connections
|
|
|
|
let db_conn = app.db_conn().context("query_admin_modify_user needs a db")?;
|
|
|
|
let db_replica = app
|
|
|
|
.db_replica()
|
|
|
|
.context("query_user_stats needs a db replica")?;
|
|
|
|
let mut redis_conn = app
|
|
|
|
.redis_conn()
|
|
|
|
.await
|
|
|
|
.context("query_admin_modify_user had a redis connection error")?
|
|
|
|
.context("query_admin_modify_user needs a redis")?;
|
|
|
|
|
|
|
|
// Try to get the user who is calling from redis (if existent) / else from the database
|
|
|
|
// TODO: Make a single query, where you retrieve the user, and directly from it the secondary user (otherwise we do two jumpy, which is unnecessary)
|
|
|
|
// get the user id first. if it is 0, we should use a cache on the app
|
|
|
|
let caller_id = get_user_id_from_params(&mut redis_conn, &db_conn, &db_replica, bearer, ¶ms).await?;
|
|
|
|
|
|
|
|
// Check if the caller is an admin (i.e. if he is in an admin table)
|
|
|
|
let admin: admin::Model = admin::Entity::find()
|
2023-01-17 22:12:40 +03:00
|
|
|
.filter(admin::Column::UserId.eq(caller_id))
|
2023-02-10 20:48:51 +03:00
|
|
|
.one(db_replica.conn())
|
2023-02-19 23:27:53 +03:00
|
|
|
.await?
|
2023-02-10 20:48:51 +03:00
|
|
|
.ok_or(AccessDenied)?;
|
2023-02-19 23:27:53 +03:00
|
|
|
|
|
|
|
// If we are here, that means an admin was found, and we can safely proceed
|
|
|
|
|
|
|
|
// Fetch the admin, and the user
|
|
|
|
let user: user::Model = user::Entity::find()
|
|
|
|
.filter(user::Column::Address.eq(user_address))
|
2023-02-10 20:48:51 +03:00
|
|
|
.one(db_replica.conn())
|
2023-02-19 23:27:53 +03:00
|
|
|
.await?
|
2023-02-10 20:48:51 +03:00
|
|
|
.ok_or(FrontendErrorResponse::BadRequest("No user with this id found".to_string()))?;
|
2023-02-19 23:27:53 +03:00
|
|
|
// Return early if the target user_tier_id is the same as the original user_tier_id
|
|
|
|
response_body.insert(
|
|
|
|
"user_tier_title",
|
2023-01-17 22:12:40 +03:00
|
|
|
serde_json::Value::Number(user.user_tier_id.into()),
|
2023-02-19 23:27:53 +03:00
|
|
|
);
|
|
|
|
|
|
|
|
// Now we can modify the user's tier
|
2023-02-10 20:48:51 +03:00
|
|
|
let new_user_tier: user_tier::Model = user_tier::Entity::find()
|
2023-02-19 23:27:53 +03:00
|
|
|
.filter(user_tier::Column::Title.eq(user_tier_title.clone()))
|
2023-02-10 20:48:51 +03:00
|
|
|
.one(db_replica.conn())
|
2023-02-19 23:27:53 +03:00
|
|
|
.await?
|
2023-02-10 20:48:51 +03:00
|
|
|
.ok_or(FrontendErrorResponse::BadRequest("User Tier name was not found".to_string()))?;
|
2023-02-19 23:27:53 +03:00
|
|
|
|
|
|
|
if user.user_tier_id == new_user_tier.id {
|
|
|
|
info!("user already has that tier");
|
|
|
|
} else {
|
2023-02-10 20:48:51 +03:00
|
|
|
let mut user = user.clone().into_active_model();
|
2023-02-19 23:27:53 +03:00
|
|
|
|
|
|
|
user.user_tier_id = sea_orm::Set(new_user_tier.id);
|
|
|
|
|
|
|
|
user.save(&db_conn).await?;
|
|
|
|
|
|
|
|
info!("user's tier changed");
|
|
|
|
}
|
|
|
|
|
2023-02-10 20:48:51 +03:00
|
|
|
// Query the login table, and get all bearer tokens by this user
|
|
|
|
let bearer_tokens = login::Entity::find()
|
|
|
|
.filter(login::Column::UserId.eq(user.id))
|
|
|
|
.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?;
|
|
|
|
}
|
2023-02-19 23:33:33 +03:00
|
|
|
|
2023-02-10 20:48:51 +03:00
|
|
|
// Now delete these tokens ...
|
|
|
|
login::Entity::delete_many()
|
|
|
|
.filter(login::Column::UserId.eq(user.id))
|
|
|
|
.exec(&db_conn)
|
|
|
|
.await?;
|
2023-02-19 23:27:53 +03:00
|
|
|
|
|
|
|
Ok(Json(&response_body).into_response())
|
|
|
|
|
|
|
|
}
|