test log in and out
This commit is contained in:
parent
645fa7328b
commit
c26d57fe5e
@ -5,8 +5,8 @@ use crate::admin_queries::query_admin_modify_usertier;
|
||||
use crate::app::Web3ProxyApp;
|
||||
use crate::errors::Web3ProxyResponse;
|
||||
use crate::errors::{Web3ProxyError, Web3ProxyErrorContext};
|
||||
use crate::frontend::users::authentication::PostLogin;
|
||||
use crate::user_token::UserBearerToken;
|
||||
use crate::PostLogin;
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
headers::{authorization::Bearer, Authorization},
|
||||
@ -35,7 +35,7 @@ use std::ops::Add;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use time_03::{Duration, OffsetDateTime};
|
||||
use tracing::{debug, info, warn};
|
||||
use tracing::{info, trace, warn};
|
||||
use ulid::Ulid;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@ -232,7 +232,7 @@ pub async fn admin_imitate_login_get(
|
||||
.filter(pending_login::Column::ExpiresAt.lte(now))
|
||||
.exec(db_conn)
|
||||
.await?;
|
||||
debug!("cleared expired pending_logins: {:?}", delete_result);
|
||||
trace!("cleared expired pending_logins: {:?}", delete_result);
|
||||
|
||||
// Note that the admin is trying to log in as this user
|
||||
let trail = admin_trail::ActiveModel {
|
||||
|
@ -48,7 +48,8 @@ use ulid::Ulid;
|
||||
use uuid::Uuid;
|
||||
|
||||
/// This lets us use UUID and ULID while we transition to only ULIDs
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Deserialize)]
|
||||
/// TODO: custom deserialize that can also go from String to Ulid
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Eq, PartialEq)]
|
||||
pub enum RpcSecretKey {
|
||||
Ulid(Ulid),
|
||||
Uuid(Uuid),
|
||||
|
@ -3,7 +3,6 @@ use crate::app::Web3ProxyApp;
|
||||
use crate::errors::{Web3ProxyError, Web3ProxyErrorContext, Web3ProxyResponse};
|
||||
use crate::frontend::authorization::{login_is_authorized, RpcSecretKey};
|
||||
use crate::user_token::UserBearerToken;
|
||||
use crate::{PostLogin, PostLoginQuery};
|
||||
use axum::{
|
||||
extract::{Path, Query},
|
||||
headers::{authorization::Bearer, Authorization},
|
||||
@ -23,6 +22,7 @@ use migration::sea_orm::{
|
||||
self, ActiveModelTrait, ColumnTrait, DatabaseTransaction, EntityTrait, IntoActiveModel,
|
||||
QueryFilter, TransactionTrait,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::json;
|
||||
use siwe::{Message, VerificationOpts};
|
||||
use std::ops::Add;
|
||||
@ -32,6 +32,24 @@ use time_03::{Duration, OffsetDateTime};
|
||||
use tracing::{error, trace, warn};
|
||||
use ulid::Ulid;
|
||||
|
||||
/// Query params for our `post_login` handler.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PostLoginQuery {
|
||||
/// While we are in alpha/beta, we require users to supply an invite code.
|
||||
/// The invite code (if any) is set in the application's config.
|
||||
pub invite_code: Option<String>,
|
||||
}
|
||||
|
||||
/// JSON body to our `post_login` handler.
|
||||
/// Currently only siwe logins that send an address, msg, and sig are allowed.
|
||||
/// Email/password and other login methods are planned.
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
pub struct PostLogin {
|
||||
pub sig: String,
|
||||
pub msg: String,
|
||||
pub referral_code: Option<String>,
|
||||
}
|
||||
|
||||
/// `GET /user/login/:user_address` or `GET /user/login/:user_address/:message_eip` -- Start the "Sign In with Ethereum" (siwe) login flow.
|
||||
///
|
||||
/// `message_eip`s accepted:
|
||||
@ -103,18 +121,22 @@ pub async fn user_login_get(
|
||||
let db_conn = app.db_conn()?;
|
||||
|
||||
// delete any expired logins
|
||||
let expired_logins = login::Entity::delete_many()
|
||||
if let Err(err) = login::Entity::delete_many()
|
||||
.filter(login::Column::ExpiresAt.lte(now))
|
||||
.exec(db_conn)
|
||||
.await;
|
||||
trace!(?expired_logins, "deleted");
|
||||
.await
|
||||
{
|
||||
warn!(?err, "expired_logins");
|
||||
};
|
||||
|
||||
// delete any expired pending logins
|
||||
let expired_pending_logins = pending_login::Entity::delete_many()
|
||||
.filter(login::Column::ExpiresAt.lte(now))
|
||||
if let Err(err) = pending_login::Entity::delete_many()
|
||||
.filter(pending_login::Column::ExpiresAt.lte(now))
|
||||
.exec(db_conn)
|
||||
.await;
|
||||
trace!(?expired_pending_logins, "deleted");
|
||||
.await
|
||||
{
|
||||
warn!(?err, "expired_pending_logins");
|
||||
};
|
||||
|
||||
// we add 1 to expire_seconds just to be sure the database has the key for the full expiration_time
|
||||
let expires_at = Utc
|
||||
@ -299,7 +321,7 @@ pub async fn user_login_post(
|
||||
let txn = db_conn.begin().await?;
|
||||
|
||||
// First, optionally catch a referral code from the parameters if there is any
|
||||
trace!("Referal code is: {:?}", payload.referral_code);
|
||||
trace!(?payload.referral_code);
|
||||
if let Some(referral_code) = payload.referral_code.as_ref() {
|
||||
// If it is not inside, also check in the database
|
||||
trace!("Using register referral code: {:?}", referral_code);
|
||||
|
@ -19,25 +19,3 @@ pub mod rpcs;
|
||||
pub mod stats;
|
||||
pub mod sub_commands;
|
||||
pub mod user_token;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
// Push some commonly used types here. Can establish a folder later on
|
||||
/// Query params for our `post_login` handler.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PostLoginQuery {
|
||||
/// While we are in alpha/beta, we require users to supply an invite code.
|
||||
/// The invite code (if any) is set in the application's config.
|
||||
/// This may eventually provide some sort of referral bonus.
|
||||
invite_code: Option<String>,
|
||||
}
|
||||
|
||||
/// JSON body to our `post_login` handler.
|
||||
/// Currently only siwe logins that send an address, msg, and sig are allowed.
|
||||
/// Email/password and other login methods are planned.
|
||||
#[derive(Debug, Deserialize)]
|
||||
pub struct PostLogin {
|
||||
sig: String,
|
||||
msg: String,
|
||||
pub referral_code: Option<String>,
|
||||
}
|
||||
|
@ -5,7 +5,7 @@ use migration::sea_query::table::ColumnDef;
|
||||
use migration::{Alias, DbErr, Migrator, MigratorTrait, Table};
|
||||
use std::time::Duration;
|
||||
use tokio::time::sleep;
|
||||
use tracing::{debug, info, trace, warn};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
pub use migration::sea_orm::DatabaseConnection;
|
||||
|
||||
|
@ -73,12 +73,13 @@ impl TestApp {
|
||||
|
||||
let anvil_provider = Provider::<Http>::try_from(anvil.endpoint()).unwrap();
|
||||
|
||||
// TODO: instead of starting a db every time, use a connection pool and transactions to begin/rollback
|
||||
let db = if setup_db {
|
||||
// sqlite doesn't seem to work. our migrations are written for mysql
|
||||
// so lets use docker to start mysql
|
||||
let password: String = rand::thread_rng()
|
||||
.sample_iter(&Alphanumeric)
|
||||
.take(32)
|
||||
.take(16)
|
||||
.map(char::from)
|
||||
.collect();
|
||||
|
||||
|
@ -2,7 +2,7 @@ mod common;
|
||||
|
||||
use crate::common::TestApp;
|
||||
|
||||
#[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
// #[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
#[ignore = "under construction"]
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_admin_imitate_user() {
|
||||
@ -11,7 +11,7 @@ async fn test_admin_imitate_user() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
// #[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
#[ignore = "under construction"]
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_admin_grant_credits() {
|
||||
@ -20,7 +20,7 @@ async fn test_admin_grant_credits() {
|
||||
todo!();
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
// #[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
#[ignore = "under construction"]
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_admin_change_user_tier() {
|
||||
|
@ -1,8 +1,24 @@
|
||||
mod common;
|
||||
|
||||
use crate::common::TestApp;
|
||||
use ethers::signers::Signer;
|
||||
use tracing::info;
|
||||
use axum::headers::Authorization;
|
||||
use ethers::{signers::Signer, types::Signature};
|
||||
use hashbrown::HashMap;
|
||||
use serde::Deserialize;
|
||||
use serde_json::Value;
|
||||
use tracing::{debug, info, trace};
|
||||
use ulid::Ulid;
|
||||
use web3_proxy::frontend::users::authentication::PostLogin;
|
||||
|
||||
/// TODO: use this type in the frontend
|
||||
#[derive(Debug, Deserialize)]
|
||||
struct LoginPostResponse {
|
||||
pub bearer_token: Ulid,
|
||||
pub rpc_keys: Value,
|
||||
/// unknown data gets put here
|
||||
#[serde(flatten, default = "HashMap::default")]
|
||||
pub extra: HashMap<String, serde_json::Value>,
|
||||
}
|
||||
|
||||
/// TODO: 191 and the other message formats in another test
|
||||
#[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
@ -10,23 +26,57 @@ use tracing::info;
|
||||
async fn test_log_in_and_out() {
|
||||
let x = TestApp::spawn(true).await;
|
||||
|
||||
let r = reqwest::Client::new();
|
||||
|
||||
let w = x.wallet(0);
|
||||
|
||||
let login_url = format!("{}user/login/{:?}", x.proxy_provider.url(), w.address());
|
||||
let login_response = reqwest::get(login_url).await.unwrap();
|
||||
let login_get_url = format!("{}user/login/{:?}", x.proxy_provider.url(), w.address());
|
||||
let login_message = r.get(login_get_url).send().await.unwrap();
|
||||
|
||||
let login_message = login_message.text().await.unwrap();
|
||||
|
||||
// sign the message and POST it
|
||||
let signed: Signature = w.sign_message(&login_message).await.unwrap();
|
||||
trace!(?signed);
|
||||
|
||||
let post_login_data = PostLogin {
|
||||
msg: login_message,
|
||||
sig: signed.to_string(),
|
||||
referral_code: None,
|
||||
};
|
||||
debug!(?post_login_data);
|
||||
|
||||
let login_post_url = format!("{}user/login", x.proxy_provider.url());
|
||||
let login_response = r
|
||||
.post(login_post_url)
|
||||
.json(&post_login_data)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<LoginPostResponse>()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
info!(?login_response);
|
||||
|
||||
// TODO: sign the message and POST it
|
||||
// use the bearer token to log out
|
||||
let logout_post_url = format!("{}user/logout", x.proxy_provider.url());
|
||||
let logout_response = r
|
||||
.post(logout_post_url)
|
||||
.bearer_auth(login_response.bearer_token)
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.text()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// TODO: get bearer token out of response
|
||||
info!(?logout_response);
|
||||
|
||||
// TODO: log out
|
||||
|
||||
todo!();
|
||||
assert_eq!(logout_response, "goodbye");
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
// #[cfg_attr(not(feature = "tests-needing-docker"), ignore)]
|
||||
#[ignore = "under construction"]
|
||||
#[test_log::test(tokio::test)]
|
||||
async fn test_referral_bonus() {
|
||||
|
Loading…
Reference in New Issue
Block a user