Add server methods for creating chat domain objects
Also, consolidate all sql into a `db` module
This commit is contained in:
+29
-50
@@ -1,7 +1,9 @@
|
||||
use super::errors::TideResultExt;
|
||||
use crate::{github, rpc, AppState, DbPool, Request, RequestExt as _};
|
||||
use super::{
|
||||
db::{self, UserId},
|
||||
errors::TideResultExt,
|
||||
};
|
||||
use crate::{github, rpc, AppState, Request, RequestExt as _};
|
||||
use anyhow::{anyhow, Context};
|
||||
use async_std::stream::StreamExt;
|
||||
use async_trait::async_trait;
|
||||
pub use oauth2::basic::BasicClient as Client;
|
||||
use oauth2::{
|
||||
@@ -14,7 +16,6 @@ use scrypt::{
|
||||
Scrypt,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
use std::{borrow::Cow, convert::TryFrom, sync::Arc};
|
||||
use surf::Url;
|
||||
use tide::Server;
|
||||
@@ -34,9 +35,6 @@ pub struct User {
|
||||
|
||||
pub struct VerifyToken;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct UserId(pub i32);
|
||||
|
||||
#[async_trait]
|
||||
impl tide::Middleware<Arc<AppState>> for VerifyToken {
|
||||
async fn handle(
|
||||
@@ -51,33 +49,28 @@ impl tide::Middleware<Arc<AppState>> for VerifyToken {
|
||||
.as_str()
|
||||
.split_whitespace();
|
||||
|
||||
let user_id: i32 = auth_header
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("missing user id in authorization header"))?
|
||||
.parse()?;
|
||||
let user_id = UserId(
|
||||
auth_header
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("missing user id in authorization header"))?
|
||||
.parse()?,
|
||||
);
|
||||
let access_token = auth_header
|
||||
.next()
|
||||
.ok_or_else(|| anyhow!("missing access token in authorization header"))?;
|
||||
|
||||
let state = request.state().clone();
|
||||
|
||||
let mut password_hashes =
|
||||
sqlx::query_scalar::<_, String>("SELECT hash FROM access_tokens WHERE user_id = $1")
|
||||
.bind(&user_id)
|
||||
.fetch_many(&state.db);
|
||||
|
||||
let mut credentials_valid = false;
|
||||
while let Some(password_hash) = password_hashes.next().await {
|
||||
if let either::Either::Right(password_hash) = password_hash? {
|
||||
if verify_access_token(&access_token, &password_hash)? {
|
||||
credentials_valid = true;
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
if credentials_valid {
|
||||
request.set_ext(UserId(user_id));
|
||||
request.set_ext(user_id);
|
||||
Ok(next.run(request).await)
|
||||
} else {
|
||||
Err(anyhow!("invalid credentials").into())
|
||||
@@ -94,25 +87,12 @@ pub trait RequestExt {
|
||||
impl RequestExt for Request {
|
||||
async fn current_user(&self) -> tide::Result<Option<User>> {
|
||||
if let Some(details) = self.session().get::<github::User>(CURRENT_GITHUB_USER) {
|
||||
#[derive(FromRow)]
|
||||
struct UserRow {
|
||||
admin: bool,
|
||||
}
|
||||
|
||||
let user_row: Option<UserRow> =
|
||||
sqlx::query_as("SELECT admin FROM users WHERE github_login = $1")
|
||||
.bind(&details.login)
|
||||
.fetch_optional(self.db())
|
||||
.await?;
|
||||
|
||||
let is_insider = user_row.is_some();
|
||||
let is_admin = user_row.map_or(false, |row| row.admin);
|
||||
|
||||
let user = self.db().get_user_by_github_login(&details.login).await?;
|
||||
Ok(Some(User {
|
||||
github_login: details.login,
|
||||
avatar_url: details.avatar_url,
|
||||
is_insider,
|
||||
is_admin,
|
||||
is_insider: user.is_some(),
|
||||
is_admin: user.map_or(false, |user| user.admin),
|
||||
}))
|
||||
} else {
|
||||
Ok(None)
|
||||
@@ -265,9 +245,9 @@ async fn get_auth_callback(mut request: Request) -> tide::Result {
|
||||
.await
|
||||
.context("failed to fetch user")?;
|
||||
|
||||
let user_id: Option<i32> = sqlx::query_scalar("SELECT id from users where github_login = $1")
|
||||
.bind(&user_details.login)
|
||||
.fetch_optional(request.db())
|
||||
let user = request
|
||||
.db()
|
||||
.get_user_by_github_login(&user_details.login)
|
||||
.await?;
|
||||
|
||||
request
|
||||
@@ -276,8 +256,8 @@ async fn get_auth_callback(mut request: Request) -> tide::Result {
|
||||
|
||||
// When signing in from the native app, generate a new access token for the current user. Return
|
||||
// a redirect so that the user's browser sends this access token to the locally-running app.
|
||||
if let Some((user_id, app_sign_in_params)) = user_id.zip(query.native_app_sign_in_params) {
|
||||
let access_token = create_access_token(request.db(), user_id).await?;
|
||||
if let Some((user, app_sign_in_params)) = user.zip(query.native_app_sign_in_params) {
|
||||
let access_token = create_access_token(request.db(), user.id()).await?;
|
||||
let native_app_public_key =
|
||||
zed_auth::PublicKey::try_from(app_sign_in_params.native_app_public_key.clone())
|
||||
.context("failed to parse app public key")?;
|
||||
@@ -287,7 +267,9 @@ async fn get_auth_callback(mut request: Request) -> tide::Result {
|
||||
|
||||
return Ok(tide::Redirect::new(&format!(
|
||||
"http://127.0.0.1:{}?user_id={}&access_token={}",
|
||||
app_sign_in_params.native_app_port, user_id, encrypted_access_token,
|
||||
app_sign_in_params.native_app_port,
|
||||
user.id().0,
|
||||
encrypted_access_token,
|
||||
))
|
||||
.into());
|
||||
}
|
||||
@@ -300,14 +282,11 @@ async fn post_sign_out(mut request: Request) -> tide::Result {
|
||||
Ok(tide::Redirect::new("/").into())
|
||||
}
|
||||
|
||||
pub async fn create_access_token(db: &DbPool, user_id: i32) -> tide::Result<String> {
|
||||
pub async fn create_access_token(db: &db::Db, user_id: UserId) -> tide::Result<String> {
|
||||
let access_token = zed_auth::random_token();
|
||||
let access_token_hash =
|
||||
hash_access_token(&access_token).context("failed to hash access token")?;
|
||||
sqlx::query("INSERT INTO access_tokens (user_id, hash) values ($1, $2)")
|
||||
.bind(user_id)
|
||||
.bind(access_token_hash)
|
||||
.fetch_optional(db)
|
||||
db.create_access_token_hash(user_id, access_token_hash)
|
||||
.await?;
|
||||
Ok(access_token)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user