Merge branch 'main' into screen-sharing

# Conflicts:
#	crates/collab/src/integration_tests.rs
#	crates/collab/src/main.rs
#	styles/src/styleTree/workspace.ts
This commit is contained in:
Antonio Scandurra 2022-10-21 14:29:45 +02:00
commit 7e411ae098
75 changed files with 2438 additions and 1903 deletions

View file

@ -76,7 +76,7 @@ pub async fn validate_api_token<B>(req: Request<B>, next: Next<B>) -> impl IntoR
let state = req.extensions().get::<Arc<AppState>>().unwrap();
if token != state.api_token {
if token != state.config.api_token {
Err(Error::Http(
StatusCode::UNAUTHORIZED,
"invalid authorization token".to_string(),
@ -88,7 +88,7 @@ pub async fn validate_api_token<B>(req: Request<B>, next: Next<B>) -> impl IntoR
#[derive(Debug, Deserialize)]
struct AuthenticatedUserParams {
github_user_id: i32,
github_user_id: Option<i32>,
github_login: String,
}
@ -104,7 +104,7 @@ async fn get_authenticated_user(
) -> Result<Json<AuthenticatedUserResponse>> {
let user = app
.db
.get_user_by_github_account(&params.github_login, Some(params.github_user_id))
.get_user_by_github_account(&params.github_login, params.github_user_id)
.await?
.ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "user not found".into()))?;
let metrics_id = app.db.get_user_metrics_id(user.id).await?;
@ -156,7 +156,7 @@ async fn create_user(
Json(params): Json<CreateUserParams>,
Extension(app): Extension<Arc<AppState>>,
Extension(rpc_server): Extension<Arc<rpc::Server>>,
) -> Result<Json<CreateUserResponse>> {
) -> Result<Json<Option<CreateUserResponse>>> {
let user = NewUserParams {
github_login: params.github_login,
github_user_id: params.github_user_id,
@ -165,7 +165,8 @@ async fn create_user(
// Creating a user via the normal signup process
let result = if let Some(email_confirmation_code) = params.email_confirmation_code {
app.db
if let Some(result) = app
.db
.create_user_from_invite(
&Invite {
email_address: params.email_address,
@ -174,6 +175,11 @@ async fn create_user(
user,
)
.await?
{
result
} else {
return Ok(Json(None));
}
}
// Creating a user as an admin
else if params.admin {
@ -200,11 +206,11 @@ async fn create_user(
.await?
.ok_or_else(|| anyhow!("couldn't find the user we just created"))?;
Ok(Json(CreateUserResponse {
Ok(Json(Some(CreateUserResponse {
user,
metrics_id: result.metrics_id,
signup_device_id: result.signup_device_id,
}))
})))
}
#[derive(Deserialize)]

View file

@ -1,7 +1,7 @@
use std::sync::Arc;
use super::db::{self, UserId};
use crate::{AppState, Error, Result};
use crate::{
db::{self, UserId},
AppState, Error, Result,
};
use anyhow::{anyhow, Context};
use axum::{
http::{self, Request, StatusCode},
@ -13,6 +13,7 @@ use scrypt::{
password_hash::{PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Scrypt,
};
use std::sync::Arc;
pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl IntoResponse {
let mut auth_header = req
@ -21,7 +22,7 @@ pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl Into
.and_then(|header| header.to_str().ok())
.ok_or_else(|| {
Error::Http(
StatusCode::BAD_REQUEST,
StatusCode::UNAUTHORIZED,
"missing authorization header".to_string(),
)
})?
@ -41,12 +42,18 @@ pub async fn validate_header<B>(mut req: Request<B>, next: Next<B>) -> impl Into
)
})?;
let state = req.extensions().get::<Arc<AppState>>().unwrap();
let mut credentials_valid = false;
for password_hash in state.db.get_access_token_hashes(user_id).await? {
if verify_access_token(access_token, &password_hash)? {
let state = req.extensions().get::<Arc<AppState>>().unwrap();
if let Some(admin_token) = access_token.strip_prefix("ADMIN_TOKEN:") {
if state.config.api_token == admin_token {
credentials_valid = true;
break;
}
} else {
for password_hash in state.db.get_access_token_hashes(user_id).await? {
if verify_access_token(access_token, &password_hash)? {
credentials_valid = true;
break;
}
}
}

View file

@ -51,7 +51,7 @@ pub trait Db: Send + Sync {
&self,
invite: &Invite,
user: NewUserParams,
) -> Result<NewUserResult>;
) -> Result<Option<NewUserResult>>;
/// Registers a new project for the given user.
async fn register_project(&self, host_user_id: UserId) -> Result<ProjectId>;
@ -482,7 +482,7 @@ impl Db for PostgresDb {
&self,
invite: &Invite,
user: NewUserParams,
) -> Result<NewUserResult> {
) -> Result<Option<NewUserResult>> {
let mut tx = self.pool.begin().await?;
let (signup_id, existing_user_id, inviting_user_id, signup_device_id): (
@ -506,10 +506,7 @@ impl Db for PostgresDb {
.ok_or_else(|| Error::Http(StatusCode::NOT_FOUND, "no such invite".to_string()))?;
if existing_user_id.is_some() {
Err(Error::Http(
StatusCode::UNPROCESSABLE_ENTITY,
"invitation already redeemed".to_string(),
))?;
return Ok(None);
}
let (user_id, metrics_id): (UserId, String) = sqlx::query_as(
@ -576,12 +573,12 @@ impl Db for PostgresDb {
}
tx.commit().await?;
Ok(NewUserResult {
Ok(Some(NewUserResult {
user_id,
metrics_id,
inviting_user_id,
signup_device_id,
})
}))
}
// invite codes
@ -1958,7 +1955,7 @@ mod test {
&self,
_invite: &Invite,
_user: NewUserParams,
) -> Result<NewUserResult> {
) -> Result<Option<NewUserResult>> {
unimplemented!()
}

View file

@ -852,6 +852,7 @@ async fn test_invite_codes() {
},
)
.await
.unwrap()
.unwrap();
let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
assert_eq!(invite_count, 1);
@ -897,6 +898,7 @@ async fn test_invite_codes() {
},
)
.await
.unwrap()
.unwrap();
let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
assert_eq!(invite_count, 0);
@ -954,6 +956,7 @@ async fn test_invite_codes() {
)
.await
.unwrap()
.unwrap()
.user_id;
let (_, invite_count) = db.get_invite_code_for_user(user1).await.unwrap().unwrap();
@ -1099,6 +1102,7 @@ async fn test_signups() {
},
)
.await
.unwrap()
.unwrap();
let user = db.get_user_by_id(user_id).await.unwrap().unwrap();
assert!(inviting_user_id.is_none());
@ -1108,19 +1112,21 @@ async fn test_signups() {
assert_eq!(signup_device_id.unwrap(), "device_id_0");
// cannot redeem the same signup again.
db.create_user_from_invite(
&Invite {
email_address: signups_batch1[0].email_address.clone(),
email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(),
},
NewUserParams {
github_login: "some-other-github_account".into(),
github_user_id: 1,
invite_count: 5,
},
)
.await
.unwrap_err();
assert!(db
.create_user_from_invite(
&Invite {
email_address: signups_batch1[0].email_address.clone(),
email_confirmation_code: signups_batch1[0].email_confirmation_code.clone(),
},
NewUserParams {
github_login: "some-other-github_account".into(),
github_user_id: 1,
invite_count: 5,
},
)
.await
.unwrap()
.is_none());
// cannot redeem a signup with the wrong confirmation code.
db.create_user_from_invite(

View file

@ -6478,8 +6478,7 @@ impl TestServer {
Arc::new(AppState {
db: test_db.db().clone(),
live_kit_client: Some(Arc::new(fake_server.create_api_client())),
api_token: Default::default(),
invite_link_prefix: Default::default(),
config: Default::default(),
})
}

View file

@ -30,8 +30,6 @@ pub struct Config {
pub database_url: String,
pub api_token: String,
pub invite_link_prefix: String,
pub honeycomb_api_key: Option<String>,
pub honeycomb_dataset: Option<String>,
pub live_kit_server: Option<String>,
pub live_kit_key: Option<String>,
pub live_kit_secret: Option<String>,
@ -41,13 +39,12 @@ pub struct Config {
pub struct AppState {
db: Arc<dyn Db>,
api_token: String,
invite_link_prefix: String,
live_kit_client: Option<Arc<dyn live_kit_server::api::Client>>,
config: Config,
}
impl AppState {
async fn new(config: &Config) -> Result<Arc<Self>> {
async fn new(config: Config) -> Result<Arc<Self>> {
let db = PostgresDb::new(&config.database_url, 5).await?;
let live_kit_client = if let Some(((server, key), secret)) = config
.live_kit_server
@ -67,8 +64,7 @@ impl AppState {
let this = Self {
db: Arc::new(db),
live_kit_client,
api_token: config.api_token.clone(),
invite_link_prefix: config.invite_link_prefix.clone(),
config,
};
Ok(Arc::new(this))
}
@ -85,9 +81,9 @@ async fn main() -> Result<()> {
let config = envy::from_env::<Config>().expect("error loading config");
init_tracing(&config);
let state = AppState::new(&config).await?;
let state = AppState::new(config).await?;
let listener = TcpListener::bind(&format!("0.0.0.0:{}", config.http_port))
let listener = TcpListener::bind(&format!("0.0.0.0:{}", state.config.http_port))
.expect("failed to bind TCP listener");
let rpc_server = rpc::Server::new(state.clone(), None);

View file

@ -396,7 +396,7 @@ impl Server {
if let Some((code, count)) = invite_code {
this.peer.send(connection_id, proto::UpdateInviteInfo {
url: format!("{}{}", this.app_state.invite_link_prefix, code),
url: format!("{}{}", this.app_state.config.invite_link_prefix, code),
count,
})?;
}
@ -562,7 +562,7 @@ impl Server {
self.peer.send(
connection_id,
proto::UpdateInviteInfo {
url: format!("{}{}", self.app_state.invite_link_prefix, &code),
url: format!("{}{}", self.app_state.config.invite_link_prefix, &code),
count: user.invite_count as u32,
},
)?;
@ -580,7 +580,10 @@ impl Server {
self.peer.send(
connection_id,
proto::UpdateInviteInfo {
url: format!("{}{}", self.app_state.invite_link_prefix, invite_code),
url: format!(
"{}{}",
self.app_state.config.invite_link_prefix, invite_code
),
count: user.invite_count as u32,
},
)?;