only require invite_code on first login
This commit is contained in:
parent
6e356cf9a7
commit
9ec8abdf49
|
@ -87,7 +87,11 @@ Flame graphs make a developer's join of finding slow code painless:
|
||||||
1
|
1
|
||||||
$ echo 0 | sudo tee /proc/sys/kernel/kptr_restrict
|
$ echo 0 | sudo tee /proc/sys/kernel/kptr_restrict
|
||||||
0
|
0
|
||||||
$ CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph
|
$ cat /proc/sys/kernel/perf_event_paranoid
|
||||||
|
4
|
||||||
|
$ echo -1 | sudo tee /proc/sys/kernel/perf_event_paranoid
|
||||||
|
0
|
||||||
|
$ CARGO_PROFILE_RELEASE_DEBUG=true cargo flamegraph --bin web3_proxy
|
||||||
|
|
||||||
|
|
||||||
## GDB
|
## GDB
|
||||||
|
|
|
@ -80,7 +80,6 @@ pub struct UserProxyResponseKey {
|
||||||
error_response: bool,
|
error_response: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// key is the (user_key_id, method, error_response)
|
|
||||||
pub type UserProxyResponseCache = Cache<
|
pub type UserProxyResponseCache = Cache<
|
||||||
UserProxyResponseKey,
|
UserProxyResponseKey,
|
||||||
Arc<ProxyResponseAggregate>,
|
Arc<ProxyResponseAggregate>,
|
||||||
|
|
|
@ -145,7 +145,7 @@ pub struct PostLoginResponse {
|
||||||
/// Used for authenticating additonal requests.
|
/// Used for authenticating additonal requests.
|
||||||
bearer_token: Ulid,
|
bearer_token: Ulid,
|
||||||
/// Used for authenticating with the RPC endpoints.
|
/// Used for authenticating with the RPC endpoints.
|
||||||
api_keys: Vec<UserKey>,
|
api_keys: HashMap<u64, UserKey>,
|
||||||
user_id: u64,
|
user_id: u64,
|
||||||
// TODO: what else?
|
// TODO: what else?
|
||||||
}
|
}
|
||||||
|
@ -162,45 +162,36 @@ pub async fn user_login_post(
|
||||||
) -> FrontendResult {
|
) -> FrontendResult {
|
||||||
login_is_authorized(&app, ip).await?;
|
login_is_authorized(&app, ip).await?;
|
||||||
|
|
||||||
if let Some(invite_code) = &app.config.invite_code {
|
|
||||||
// we don't do per-user referral codes because we shouldn't collect what we don't need.
|
|
||||||
// we don't need to build a social graph between addresses like that.
|
|
||||||
if query.invite_code.as_ref() != Some(invite_code) {
|
|
||||||
warn!("if address is already registered, allow login! else, error");
|
|
||||||
|
|
||||||
// TODO: this return doesn't seem right
|
|
||||||
return Err(anyhow::anyhow!("checking invite_code"))?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we can't trust that they didn't tamper with the message in some way
|
// we can't trust that they didn't tamper with the message in some way
|
||||||
// TODO: it seems like some clients do things unexpectedly. these don't always parse
|
// TODO: it seems like some clients do things unexpectedly. these don't always parse
|
||||||
// let their_msg: siwe::Message = payload.msg.parse().context("parsing user's message")?;
|
// let their_msg: siwe::Message = payload.msg.parse().context("parsing user's message")?;
|
||||||
|
|
||||||
// TODO: do this safely
|
// TODO: this seems too verbose. how can we simply convert a String into a [u8; 65]
|
||||||
let their_sig_bytes = Bytes::from_str(&payload.sig).context("parsing sig")?;
|
let their_sig_bytes = Bytes::from_str(&payload.sig).context("parsing sig")?;
|
||||||
if their_sig_bytes.len() != 65 {
|
if their_sig_bytes.len() != 65 {
|
||||||
return Err(anyhow::anyhow!("checking signature length"))?;
|
return Err(anyhow::anyhow!("checking signature length").into());
|
||||||
}
|
}
|
||||||
let mut their_sig: [u8; 65] = [0; 65];
|
let mut their_sig: [u8; 65] = [0; 65];
|
||||||
for x in 0..65 {
|
for x in 0..65 {
|
||||||
their_sig[x] = their_sig_bytes[x]
|
their_sig[x] = their_sig_bytes[x]
|
||||||
}
|
}
|
||||||
|
|
||||||
let their_msg: String = if payload.msg.starts_with("0x") {
|
// TODO: checking 0x seems fragile, but I think it will be fine
|
||||||
|
let their_msg: Message = if payload.msg.starts_with("0x") {
|
||||||
let their_msg_bytes = Bytes::from_str(&payload.msg).context("parsing payload message")?;
|
let their_msg_bytes = Bytes::from_str(&payload.msg).context("parsing payload message")?;
|
||||||
|
|
||||||
// TODO: lossy or no?
|
// TODO: lossy or no?
|
||||||
String::from_utf8_lossy(their_msg_bytes.as_ref()).to_string()
|
String::from_utf8_lossy(their_msg_bytes.as_ref())
|
||||||
|
.parse::<siwe::Message>()
|
||||||
|
.context("parsing hex string message")?
|
||||||
} else {
|
} else {
|
||||||
payload.msg
|
payload
|
||||||
|
.msg
|
||||||
|
.parse::<siwe::Message>()
|
||||||
|
.context("parsing string message")?
|
||||||
};
|
};
|
||||||
|
|
||||||
let their_msg = their_msg
|
// TODO: this is fragile. have a helper function/struct for redis keys
|
||||||
.parse::<siwe::Message>()
|
|
||||||
.context("parsing string message")?;
|
|
||||||
|
|
||||||
// TODO: this is fragile
|
|
||||||
let login_nonce_key = format!("login_nonce:{}", &their_msg.nonce);
|
let login_nonce_key = format!("login_nonce:{}", &their_msg.nonce);
|
||||||
|
|
||||||
// fetch the message we gave them from our redis
|
// fetch the message we gave them from our redis
|
||||||
|
@ -235,12 +226,11 @@ pub async fn user_login_post(
|
||||||
"both the primary and eip191 verification failed: {:#?}; {:#?}",
|
"both the primary and eip191 verification failed: {:#?}; {:#?}",
|
||||||
err_1,
|
err_1,
|
||||||
err_191
|
err_191
|
||||||
))?;
|
)
|
||||||
|
.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let bearer_token = Ulid::new();
|
|
||||||
|
|
||||||
let db_conn = app.db_conn().context("Getting database connection")?;
|
let db_conn = app.db_conn().context("Getting database connection")?;
|
||||||
|
|
||||||
// TODO: limit columns or load whole user?
|
// TODO: limit columns or load whole user?
|
||||||
|
@ -250,8 +240,17 @@ pub async fn user_login_post(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let (u, _uks, response) = match u {
|
let (u, uks, status_code) = match u {
|
||||||
None => {
|
None => {
|
||||||
|
// user does not exist yet
|
||||||
|
|
||||||
|
// check the invite code
|
||||||
|
if let Some(invite_code) = &app.config.invite_code {
|
||||||
|
if query.invite_code.as_ref() != Some(invite_code) {
|
||||||
|
return Err(anyhow::anyhow!("checking invite_code").into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let txn = db_conn.begin().await?;
|
let txn = db_conn.begin().await?;
|
||||||
|
|
||||||
// the only thing we need from them is an address
|
// the only thing we need from them is an address
|
||||||
|
@ -263,8 +262,11 @@ pub async fn user_login_post(
|
||||||
|
|
||||||
let u = u.insert(&txn).await?;
|
let u = u.insert(&txn).await?;
|
||||||
|
|
||||||
|
// create the user's first api key
|
||||||
|
// TODO: rename to UserApiKey? RpcApiKey?
|
||||||
let user_key = UserKey::new();
|
let user_key = UserKey::new();
|
||||||
|
|
||||||
|
// TODO: variable requests per minute depending on the invite code
|
||||||
let uk = user_keys::ActiveModel {
|
let uk = user_keys::ActiveModel {
|
||||||
user_id: sea_orm::Set(u.id),
|
user_id: sea_orm::Set(u.id),
|
||||||
api_key: sea_orm::Set(user_key.into()),
|
api_key: sea_orm::Set(user_key.into()),
|
||||||
|
@ -272,7 +274,6 @@ pub async fn user_login_post(
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: if this fails, revert adding the user, too
|
|
||||||
let uk = uk
|
let uk = uk
|
||||||
.insert(&txn)
|
.insert(&txn)
|
||||||
.await
|
.await
|
||||||
|
@ -280,17 +281,10 @@ pub async fn user_login_post(
|
||||||
|
|
||||||
let uks = vec![uk];
|
let uks = vec![uk];
|
||||||
|
|
||||||
|
// save the user and key to the database
|
||||||
txn.commit().await?;
|
txn.commit().await?;
|
||||||
|
|
||||||
let response_json = PostLoginResponse {
|
(u, uks, StatusCode::CREATED)
|
||||||
api_keys: uks.iter().map(|uk| uk.api_key.into()).collect(),
|
|
||||||
bearer_token,
|
|
||||||
user_id: u.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = (StatusCode::CREATED, Json(response_json)).into_response();
|
|
||||||
|
|
||||||
(u, uks, response)
|
|
||||||
}
|
}
|
||||||
Some(u) => {
|
Some(u) => {
|
||||||
// the user is already registered
|
// the user is already registered
|
||||||
|
@ -300,19 +294,26 @@ pub async fn user_login_post(
|
||||||
.await
|
.await
|
||||||
.context("failed loading user's key")?;
|
.context("failed loading user's key")?;
|
||||||
|
|
||||||
let response_json = PostLoginResponse {
|
(u, uks, StatusCode::OK)
|
||||||
api_keys: uks.iter().map(|uk| uk.api_key.into()).collect(),
|
|
||||||
bearer_token,
|
|
||||||
user_id: u.id,
|
|
||||||
};
|
|
||||||
|
|
||||||
let response = (StatusCode::OK, Json(response_json)).into_response();
|
|
||||||
|
|
||||||
(u, uks, response)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// create a bearer token for the user.
|
||||||
|
let bearer_token = Ulid::new();
|
||||||
|
|
||||||
|
let response_json = PostLoginResponse {
|
||||||
|
api_keys: uks
|
||||||
|
.into_iter()
|
||||||
|
.map(|uk| (uk.id, uk.api_key.into()))
|
||||||
|
.collect(),
|
||||||
|
bearer_token,
|
||||||
|
user_id: u.id,
|
||||||
|
};
|
||||||
|
|
||||||
|
let response = (status_code, Json(response_json)).into_response();
|
||||||
|
|
||||||
// add bearer to redis
|
// add bearer to redis
|
||||||
|
// TODO: use a helper function/struct for this
|
||||||
let bearer_redis_key = format!("bearer:{}", bearer_token);
|
let bearer_redis_key = format!("bearer:{}", bearer_token);
|
||||||
|
|
||||||
// expire in 4 weeks
|
// expire in 4 weeks
|
||||||
|
@ -353,7 +354,7 @@ pub async fn user_logout_post(
|
||||||
/// the JSON input to the `post_user` handler
|
/// the JSON input to the `post_user` handler
|
||||||
/// This handles updating
|
/// This handles updating
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
pub struct PostUser {
|
pub struct UserProfilePost {
|
||||||
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>,
|
||||||
|
@ -365,7 +366,7 @@ pub async fn user_profile_post(
|
||||||
TypedHeader(Authorization(bearer_token)): TypedHeader<Authorization<Bearer>>,
|
TypedHeader(Authorization(bearer_token)): TypedHeader<Authorization<Bearer>>,
|
||||||
ClientIp(ip): ClientIp,
|
ClientIp(ip): ClientIp,
|
||||||
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
||||||
Json(payload): Json<PostUser>,
|
Json(payload): Json<UserProfilePost>,
|
||||||
) -> FrontendResult {
|
) -> FrontendResult {
|
||||||
login_is_authorized(&app, ip).await?;
|
login_is_authorized(&app, ip).await?;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue