Skip to content

Commit

Permalink
test: sign in url test (AppFlowy-IO#75)
Browse files Browse the repository at this point in the history
* test: sign in url test

* fix: Tungstenite allows remote attackers to cause a denial of service

* chore: enable feature

* chore: update
  • Loading branch information
appflowy authored Sep 24, 2023
1 parent e3ff765 commit 0883ae9
Show file tree
Hide file tree
Showing 13 changed files with 90 additions and 140 deletions.
9 changes: 5 additions & 4 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,11 @@ once_cell = "1.7.2"
collab-plugins = { version = "0.1.0", features = ["sync_plugin"] }
collab-define = { version = "0.1.0" }
collab-sync-protocol = { version = "0.1.0" }
client-api = { path = "libs/client-api" }
tempfile = "3.4.0"
assert-json-diff = "2.0.2"
dotenv = "0.15.0"
scraper = "0.17.1"
client-api = { path = "libs/client-api", features = ["client-api-test"]}

[[bin]]
name = "appflowy_cloud"
Expand Down
10 changes: 8 additions & 2 deletions libs/client-api/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ parking_lot = "0.12.1"
tracing = { version = "0.1" }
thiserror = "1.0.39"
serde = { version = "1.0", features = ["derive"] }
tokio-tungstenite = { version = "0.20" }
tokio-tungstenite = { version = "0.20.1" }
tokio = { version = "1.26", features = ["full"] }
futures-util = "0.3.26"
tokio-retry = "0.3"
bytes = "1.0"
uuid = "1.4.1"

scraper = { version = "0.17.1", optional = true }
collab-sync-protocol = { version = "0.1.0" }

[dev-dependencies]
scraper = "0.17.1"

[features]
client-api-test = ["scraper"]
4 changes: 2 additions & 2 deletions libs/client-api/src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@ use storage_entity::{AFWorkspaces, QueryCollabParams};
use storage_entity::{DeleteCollabParams, RawData};

pub struct Client {
cloud_client: reqwest::Client,
gotrue_client: gotrue::api::Client,
pub(crate) cloud_client: reqwest::Client,
pub(crate) gotrue_client: gotrue::api::Client,
base_url: String,
ws_addr: String,
token: Arc<RwLock<ClientToken>>,
Expand Down
51 changes: 51 additions & 0 deletions libs/client-api/src/http_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use crate::Client;
use gotrue::grant::{Grant, PasswordGrant};
use gotrue::params::GenerateLinkParams;
use gotrue_entity::GoTrueError;
use scraper::{Html, Selector};

impl Client {
pub async fn generate_sign_in_url(
&self,
admin_user_email: &str,
admin_user_password: &str,
user_email: &str,
) -> Result<String, GoTrueError> {
let admin_token = self
.gotrue_client
.token(&Grant::Password(PasswordGrant {
email: admin_user_email.to_string(),
password: admin_user_password.to_string(),
}))
.await?;

let admin_user_params: GenerateLinkParams = GenerateLinkParams {
email: user_email.to_string(),
..Default::default()
};

let link_resp = self
.gotrue_client
.generate_link(&admin_token.access_token, &admin_user_params)
.await?;
assert_eq!(link_resp.email, user_email);

let action_link = link_resp.action_link;
let resp = reqwest::Client::new().get(action_link).send().await?;
let resp_text = resp.text().await?;
Ok(extract_appflowy_sign_in_url(&resp_text))
}
}

pub fn extract_appflowy_sign_in_url(html_str: &str) -> String {
let fragment = Html::parse_fragment(html_str);
let selector = Selector::parse("a").unwrap();
fragment
.select(&selector)
.next()
.unwrap()
.value()
.attr("href")
.unwrap()
.to_string()
}
4 changes: 4 additions & 0 deletions libs/client-api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
mod http;

#[cfg(feature = "client-api-test")]
pub mod http_test;

pub mod notify;
pub mod ws;

Expand Down
1 change: 1 addition & 0 deletions src/api/collaborate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ async fn retrieve_snapshot_data_handler(
Ok(Json(AppResponse::Ok().with_data(data)))
}

#[tracing::instrument(level = "debug", skip_all)]
async fn retrieve_snapshots_handler(
payload: Json<QueryObjectSnapshotParams>,
storage: Data<Storage<CollabStorageProxy>>,
Expand Down
4 changes: 4 additions & 0 deletions src/api/user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ async fn verify_handler(
Ok(AppResponse::Ok().with_data(resp).into())
}

#[tracing::instrument(level = "debug", skip(state))]
async fn profile_handler(
uuid: UserUuid,
state: Data<AppState>,
Expand All @@ -59,6 +60,7 @@ async fn update_handler(
Ok(AppResponse::Ok().into())
}

#[tracing::instrument(level = "debug", skip_all)]
async fn login_handler(
req: Json<LoginRequest>,
state: Data<AppState>,
Expand All @@ -83,6 +85,7 @@ async fn login_handler(
Ok(HttpResponse::Ok().json(resp))
}

#[tracing::instrument(level = "debug", skip(state))]
async fn logout_handler(req: HttpRequest, state: Data<AppState>) -> Result<HttpResponse> {
let logged_user = logged_user_from_request(&req, &state.config.application.server_key)?;
logout(logged_user, state.user.clone()).await;
Expand All @@ -109,6 +112,7 @@ async fn register_handler(
Ok(HttpResponse::Ok().json(resp))
}

#[tracing::instrument(level = "debug", skip_all)]
async fn change_password_handler(
req: HttpRequest,
payload: Json<ChangePasswordRequest>,
Expand Down
34 changes: 0 additions & 34 deletions tests/client/login.rs

This file was deleted.

40 changes: 0 additions & 40 deletions tests/client/password.rs

This file was deleted.

41 changes: 0 additions & 41 deletions tests/client/register.rs

This file was deleted.

15 changes: 13 additions & 2 deletions tests/client/sign_in.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use shared_entity::error_code::ErrorCode;

use crate::client::utils::{generate_unique_email, generate_unique_registered_user};
use crate::client::utils::{generate_unique_email, generate_unique_registered_user, ADMIN_USER};
use crate::client_api_client;

#[tokio::test]
Expand Down Expand Up @@ -87,7 +87,7 @@ async fn sign_in_success() {
}

#[tokio::test]
async fn sign_in_with_url() {
async fn sign_in_with_invalid_url() {
let url_str = "appflowy-flutter://#access_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2OTQ1ODIyMjMsInN1YiI6Ijk5MGM2NDNjLTMyMWEtNGNmMi04OWY1LTNhNmJhZGFjMTg5NCIsImVtYWlsIjoiNG5uaWhpbGF0ZWRAZ21haWwuY29tIiwicGhvbmUiOiIiLCJhcHBfbWV0YWRhdGEiOnsicHJvdmlkZXIiOiJnb29nbGUiLCJwcm92aWRlcnMiOlsiZ29vZ2xlIl19LCJ1c2VyX21ldGFkYXRhIjp7ImF2YXRhcl91cmwiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NJdGZpa28xX0lpMmZiNzM4VnpGekViLVBqT0NCY3FUQzdrNjVIX0hnRTQwOVk9czk2LWMiLCJlbWFpbCI6IjRubmloaWxhdGVkQGdtYWlsLmNvbSIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJmdWxsX25hbWUiOiJmdSB6aXhpYW5nIiwiaXNzIjoiaHR0cHM6Ly93d3cuZ29vZ2xlYXBpcy5jb20vdXNlcmluZm8vdjIvbWUiLCJuYW1lIjoiZnUgeml4aWFuZyIsInBpY3R1cmUiOiJodHRwczovL2xoMy5nb29nbGV1c2VyY29udGVudC5jb20vYS9BQ2c4b2NJdGZpa28xX0lpMmZiNzM4VnpGekViLVBqT0NCY3FUQzdrNjVIX0hnRTQwOVk9czk2LWMiLCJwcm92aWRlcl9pZCI6IjEwMTQ5OTYxMDMxOTYxNjE0NTcyNSIsInN1YiI6IjEwMTQ5OTYxMDMxOTYxNjE0NTcyNSJ9LCJyb2xlIjoiIn0.I-7j-Tdj62P56zhzEqvBc7cHMldv5MA_MM7xtrBibbE&expires_in=3600&provider_token=ya29.a0AfB_byCovXs1CUiC9_f9VBTupQPsIxwh9aSlOg0PLYJvv1x1zvVfssrQfW6_Aq9no7EKpCzFUCLElOvK1Xz4x4K5r7tug79tr5b1yiOoUMWTeWTXyV61fZHQbZ9vscAiyKYtq5NqYTiytHcQEFlKr7UMfu6BTbKsUwaCgYKAaISARISFQGOcNnC0Vsx2QCAXgYO3XbfcF91WQ0169&refresh_token=Hi3Jc3I_pj9YrexcR91i5g&token_type=bearer";
let c = client_api_client();
match c.sign_in_url(url_str).await {
Expand All @@ -100,3 +100,14 @@ async fn sign_in_with_url() {
},
}
}

#[tokio::test]
async fn sign_in_with_url() {
let c = client_api_client();
let user_email = generate_unique_email();
let url_str = c
.generate_sign_in_url(&ADMIN_USER.email, &ADMIN_USER.password, &user_email)
.await
.unwrap();
let _ = c.sign_in_url(&url_str).await.unwrap();
}
15 changes: 1 addition & 14 deletions tests/gotrue/admin.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
use client_api::http_test::extract_appflowy_sign_in_url;
use gotrue::{
api::Client,
grant::{Grant, PasswordGrant},
params::{AdminUserParams, GenerateLinkParams},
};
use scraper::{Html, Selector};

use crate::{
client::{
Expand Down Expand Up @@ -95,16 +95,3 @@ async fn admin_generate_link_and_user_sign_in() {
let workspaces = client.workspaces().await.unwrap();
assert_eq!(workspaces.len(), 1);
}

fn extract_appflowy_sign_in_url(html_str: &str) -> String {
let fragment = Html::parse_fragment(html_str);
let selector = Selector::parse("a").unwrap();
fragment
.select(&selector)
.next()
.unwrap()
.value()
.attr("href")
.unwrap()
.to_string()
}

0 comments on commit 0883ae9

Please sign in to comment.