first commit adding admin endpoint to modify users tier. TODO: clear cache
This commit is contained in:
parent
7d0ac54019
commit
dc11cc89f7
2
rust-toolchain.toml
Normal file
2
rust-toolchain.toml
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[toolchain]
|
||||||
|
channel = "1.65.0"
|
@ -85,6 +85,7 @@ pub async fn serve(port: u16, proxy_app: Arc<Web3ProxyApp>) -> anyhow::Result<()
|
|||||||
get(users::user_stats_aggregated_get),
|
get(users::user_stats_aggregated_get),
|
||||||
)
|
)
|
||||||
.route("/user/stats/detailed", get(users::user_stats_detailed_get))
|
.route("/user/stats/detailed", get(users::user_stats_detailed_get))
|
||||||
|
.route("/user/modify_role", get(users::admin_change_user_roles))
|
||||||
.route("/user/logout", post(users::user_logout_post))
|
.route("/user/logout", post(users::user_logout_post))
|
||||||
.route("/status", get(status::status))
|
.route("/status", get(status::status))
|
||||||
// layers are ordered bottom up
|
// layers are ordered bottom up
|
||||||
|
@ -3,10 +3,11 @@
|
|||||||
use super::authorization::{login_is_authorized, RpcSecretKey};
|
use super::authorization::{login_is_authorized, RpcSecretKey};
|
||||||
use super::errors::FrontendResult;
|
use super::errors::FrontendResult;
|
||||||
use crate::app::Web3ProxyApp;
|
use crate::app::Web3ProxyApp;
|
||||||
use crate::user_queries::get_page_from_params;
|
use crate::user_queries::{get_page_from_params, get_user_id_from_params};
|
||||||
use crate::user_queries::{
|
use crate::user_queries::{
|
||||||
get_chain_id_from_params, get_query_start_from_params, query_user_stats, StatResponse,
|
get_chain_id_from_params, get_query_start_from_params, query_user_stats, StatResponse,
|
||||||
};
|
};
|
||||||
|
use entities::prelude::{User, SecondaryUser};
|
||||||
use crate::user_token::UserBearerToken;
|
use crate::user_token::UserBearerToken;
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use axum::headers::{Header, Origin, Referer, UserAgent};
|
use axum::headers::{Header, Origin, Referer, UserAgent};
|
||||||
@ -19,14 +20,14 @@ use axum::{
|
|||||||
use axum_client_ip::ClientIp;
|
use axum_client_ip::ClientIp;
|
||||||
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;
|
use entities::sea_orm_active_enums::{LogLevel, Role};
|
||||||
use entities::{login, pending_login, revert_log, rpc_key, user};
|
use entities::{login, pending_login, revert_log, rpc_key, secondary_user, user, user_tier};
|
||||||
use ethers::{prelude::Address, types::Bytes};
|
use ethers::{prelude::Address, types::Bytes};
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use http::{HeaderValue, StatusCode};
|
use http::{HeaderValue, StatusCode};
|
||||||
use ipnet::IpNet;
|
use ipnet::IpNet;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use log::{debug, warn};
|
use log::{debug, info, warn};
|
||||||
use migration::sea_orm::prelude::Uuid;
|
use migration::sea_orm::prelude::Uuid;
|
||||||
use migration::sea_orm::{
|
use migration::sea_orm::{
|
||||||
self, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, PaginatorTrait, QueryFilter,
|
self, ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, PaginatorTrait, QueryFilter,
|
||||||
@ -40,6 +41,7 @@ use std::str::FromStr;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use time::{Duration, OffsetDateTime};
|
use time::{Duration, OffsetDateTime};
|
||||||
use ulid::Ulid;
|
use ulid::Ulid;
|
||||||
|
use crate::frontend::errors::FrontendErrorResponse;
|
||||||
|
|
||||||
/// `GET /user/login/:user_address` or `GET /user/login/:user_address/:message_eip` -- Start the "Sign In with Ethereum" (siwe) login flow.
|
/// `GET /user/login/:user_address` or `GET /user/login/:user_address/:message_eip` -- Start the "Sign In with Ethereum" (siwe) login flow.
|
||||||
///
|
///
|
||||||
@ -853,3 +855,135 @@ pub async fn user_stats_detailed_get(
|
|||||||
|
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `GET /user/stats/detailed` -- Use a bearer token to get the user's key stats such as bandwidth used and methods requested.
|
||||||
|
///
|
||||||
|
/// If no bearer is provided, detailed stats for all users will be shown.
|
||||||
|
/// View a single user with `?user_id=$x`.
|
||||||
|
/// View a single chain with `?chain_id=$x`.
|
||||||
|
///
|
||||||
|
/// Set `$x` to zero to see all.
|
||||||
|
///
|
||||||
|
/// TODO: this will change as we add better support for secondary users.
|
||||||
|
#[debug_handler]
|
||||||
|
pub async fn admin_change_user_roles(
|
||||||
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||||
|
bearer: Option<TypedHeader<Authorization<Bearer>>>,
|
||||||
|
Query(params): Query<HashMap<String, String>>,
|
||||||
|
) -> FrontendResult {
|
||||||
|
|
||||||
|
// Make sure that the bearer exists, and has admin rights ...
|
||||||
|
let user_address: Vec<u8> = params
|
||||||
|
.get("user_address")
|
||||||
|
.ok_or_else(||
|
||||||
|
FrontendErrorResponse::StatusCode(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Unable to find user_address key in request".to_string(),
|
||||||
|
None,
|
||||||
|
)
|
||||||
|
)?
|
||||||
|
.parse::<Address>()
|
||||||
|
.map_err(|err| {
|
||||||
|
FrontendErrorResponse::StatusCode(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Unable to parse user_address as an Address".to_string(),
|
||||||
|
Some(err.into()),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.to_fixed_bytes().into();
|
||||||
|
let user_tier_title = params
|
||||||
|
.get("user_tier_title")
|
||||||
|
.ok_or_else(|| FrontendErrorResponse::StatusCode(
|
||||||
|
StatusCode::BAD_REQUEST,
|
||||||
|
"Unable to get the user_tier_title key from the request".to_string(),
|
||||||
|
None,
|
||||||
|
))?;
|
||||||
|
|
||||||
|
// Create database connections and all that
|
||||||
|
let db_conn = app.db_conn().context("admin_change_user_roles needs a db")?;
|
||||||
|
let db_replica = app
|
||||||
|
.db_replica()
|
||||||
|
.context("admin_change_user_roles needs a db replica")?;
|
||||||
|
let mut redis_conn = app
|
||||||
|
.redis_conn()
|
||||||
|
.await
|
||||||
|
.context("admin_change_user_roles had a redis connection error")?
|
||||||
|
.context("admin_change_user_roles needs a redis")?;
|
||||||
|
|
||||||
|
// 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 user_id = get_user_id_from_params(&mut redis_conn, &db_conn, &db_replica, bearer, ¶ms).await?;
|
||||||
|
|
||||||
|
let mut response_body = HashMap::new();
|
||||||
|
response_body.insert(
|
||||||
|
"user_id",
|
||||||
|
serde_json::Value::Number(user_id.into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get both the user and user role
|
||||||
|
let user: user::Model = user::Entity::find_by_id(user_id)
|
||||||
|
.one(&db_conn)
|
||||||
|
.await?
|
||||||
|
.context("No user with this id found!")?;
|
||||||
|
// TODO: Let's connect the string, and find the previous string of the user id ... (this might be ok for now too thought)
|
||||||
|
response_body.insert(
|
||||||
|
"previous_tier",
|
||||||
|
serde_json::Value::Number(user.user_tier_id.into()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Modify the user-role ...
|
||||||
|
// Check if this use has admin privileges ...
|
||||||
|
let user_role: secondary_user::Model = secondary_user::Entity::find()
|
||||||
|
.filter(secondary_user::Column::UserId.eq(user_id))
|
||||||
|
.one(&db_conn)
|
||||||
|
.await?
|
||||||
|
.context("No user tier found with that name")?;
|
||||||
|
println!("User role is: {:?}", user_role);
|
||||||
|
|
||||||
|
// Return error if the user is not an admin or a user
|
||||||
|
match user_role.role {
|
||||||
|
Role::Owner | Role::Admin => {
|
||||||
|
// Change the user tier, we can copy a bunch of the functionality from the user-tier address
|
||||||
|
|
||||||
|
// Check if all the required parameters are included in the request, if not, return an error
|
||||||
|
let user = user::Entity::find()
|
||||||
|
.filter(user::Column::Address.eq(user_address))
|
||||||
|
.one(&db_conn)
|
||||||
|
.await?
|
||||||
|
.context("No user found with that key")?;
|
||||||
|
|
||||||
|
// TODO: don't serialize the rpc key
|
||||||
|
debug!("user: {:#?}", user);
|
||||||
|
|
||||||
|
let user_tier = user_tier::Entity::find()
|
||||||
|
.filter(user_tier::Column::Title.eq(user_tier_title.clone()))
|
||||||
|
.one(&db_conn)
|
||||||
|
.await?
|
||||||
|
.context("No user tier found with that name")?;
|
||||||
|
debug!("user_tier: {:#?}", user_tier);
|
||||||
|
|
||||||
|
if user.user_tier_id == user_tier.id {
|
||||||
|
info!("user already has that tier");
|
||||||
|
} else {
|
||||||
|
let mut user = user.into_active_model();
|
||||||
|
|
||||||
|
user.user_tier_id = sea_orm::Set(user_tier.id);
|
||||||
|
|
||||||
|
user.save(&db_conn).await?;
|
||||||
|
|
||||||
|
info!("user's tier changed");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
Role::Collaborator => {
|
||||||
|
return Err(anyhow::anyhow!("you do not have admin rights!").into());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
response_body.insert(
|
||||||
|
"user_id",
|
||||||
|
serde_json::Value::Number(user_id.into()),
|
||||||
|
);
|
||||||
|
let mut response = Json(&response_body).into_response();
|
||||||
|
Ok(response)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user