sqlx: update dependency and protect against injections

This commit is contained in:
Valentin Tolmer
2022-03-22 20:45:59 +01:00
committed by nitnelave
parent bafb1dc5cc
commit 5e2eea0d97
10 changed files with 496 additions and 309 deletions

View File

@@ -55,8 +55,8 @@ pub async fn init_table(pool: &Pool) -> sqlx::Result<()> {
.foreign_key(
ForeignKey::create()
.name("JwtRefreshStorageUserForeignKey")
.table(JwtRefreshStorage::Table, Users::Table)
.col(JwtRefreshStorage::UserId, Users::UserId)
.from(JwtRefreshStorage::Table, JwtRefreshStorage::UserId)
.to(Users::Table, Users::UserId)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
@@ -94,8 +94,8 @@ pub async fn init_table(pool: &Pool) -> sqlx::Result<()> {
.foreign_key(
ForeignKey::create()
.name("JwtStorageUserForeignKey")
.table(JwtStorage::Table, Users::Table)
.col(JwtStorage::UserId, Users::UserId)
.from(JwtStorage::Table, JwtStorage::UserId)
.to(Users::Table, Users::UserId)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
@@ -127,8 +127,8 @@ pub async fn init_table(pool: &Pool) -> sqlx::Result<()> {
.foreign_key(
ForeignKey::create()
.name("PasswordResetTokensUserForeignKey")
.table(PasswordResetTokens::Table, Users::Table)
.col(PasswordResetTokens::UserId, Users::UserId)
.from(PasswordResetTokens::Table, PasswordResetTokens::UserId)
.to(Users::Table, Users::UserId)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)

View File

@@ -3,7 +3,8 @@ use crate::domain::{error::*, handler::UserId, sql_backend_handler::SqlBackendHa
use async_trait::async_trait;
use futures_util::StreamExt;
use sea_query::{Expr, Iden, Query, SimpleExpr};
use sqlx::Row;
use sea_query_binder::SqlxBinder;
use sqlx::{query_as_with, query_with, Row};
use std::collections::HashSet;
fn gen_random_string(len: usize) -> String {
@@ -19,12 +20,12 @@ fn gen_random_string(len: usize) -> String {
#[async_trait]
impl TcpBackendHandler for SqlBackendHandler {
async fn get_jwt_blacklist(&self) -> anyhow::Result<HashSet<u64>> {
let query = Query::select()
let (query, values) = Query::select()
.column(JwtStorage::JwtHash)
.from(JwtStorage::Table)
.to_string(DbQueryBuilder {});
.build_sqlx(DbQueryBuilder {});
sqlx::query(&query)
query_with(&query, values)
.map(|row: DbRow| row.get::<i64, _>(&*JwtStorage::JwtHash.to_string()) as u64)
.fetch(&self.sql_pool)
.collect::<Vec<sqlx::Result<u64>>>()
@@ -45,7 +46,7 @@ impl TcpBackendHandler for SqlBackendHandler {
s.finish()
};
let duration = chrono::Duration::days(30);
let query = Query::insert()
let (query, values) = Query::insert()
.into_table(JwtRefreshStorage::Table)
.columns(vec![
JwtRefreshStorage::RefreshTokenHash,
@@ -57,71 +58,75 @@ impl TcpBackendHandler for SqlBackendHandler {
user.into(),
(chrono::Utc::now() + duration).naive_utc().into(),
])
.to_string(DbQueryBuilder {});
sqlx::query(&query).execute(&self.sql_pool).await?;
.build_sqlx(DbQueryBuilder {});
query_with(&query, values).execute(&self.sql_pool).await?;
Ok((refresh_token, duration))
}
async fn check_token(&self, refresh_token_hash: u64, user: &UserId) -> Result<bool> {
let query = Query::select()
let (query, values) = Query::select()
.expr(SimpleExpr::Value(1.into()))
.from(JwtRefreshStorage::Table)
.and_where(Expr::col(JwtRefreshStorage::RefreshTokenHash).eq(refresh_token_hash as i64))
.and_where(Expr::col(JwtRefreshStorage::UserId).eq(user))
.to_string(DbQueryBuilder {});
Ok(sqlx::query(&query)
.build_sqlx(DbQueryBuilder {});
Ok(query_with(&query, values)
.fetch_optional(&self.sql_pool)
.await?
.is_some())
}
async fn blacklist_jwts(&self, user: &UserId) -> Result<HashSet<u64>> {
use sqlx::Result;
let query = Query::select()
let (query, values) = Query::select()
.column(JwtStorage::JwtHash)
.from(JwtStorage::Table)
.and_where(Expr::col(JwtStorage::UserId).eq(user))
.and_where(Expr::col(JwtStorage::Blacklisted).eq(true))
.to_string(DbQueryBuilder {});
let result = sqlx::query(&query)
.build_sqlx(DbQueryBuilder {});
let result = query_with(&query, values)
.map(|row: DbRow| row.get::<i64, _>(&*JwtStorage::JwtHash.to_string()) as u64)
.fetch(&self.sql_pool)
.collect::<Vec<sqlx::Result<u64>>>()
.await
.into_iter()
.collect::<Result<HashSet<u64>>>();
let query = Query::update()
let (query, values) = Query::update()
.table(JwtStorage::Table)
.values(vec![(JwtStorage::Blacklisted, true.into())])
.and_where(Expr::col(JwtStorage::UserId).eq(user))
.to_string(DbQueryBuilder {});
sqlx::query(&query).execute(&self.sql_pool).await?;
.build_sqlx(DbQueryBuilder {});
query_with(&query, values).execute(&self.sql_pool).await?;
Ok(result?)
}
async fn delete_refresh_token(&self, refresh_token_hash: u64) -> Result<()> {
let query = Query::delete()
let (query, values) = Query::delete()
.from_table(JwtRefreshStorage::Table)
.and_where(Expr::col(JwtRefreshStorage::RefreshTokenHash).eq(refresh_token_hash))
.to_string(DbQueryBuilder {});
sqlx::query(&query).execute(&self.sql_pool).await?;
.build_sqlx(DbQueryBuilder {});
query_with(&query, values).execute(&self.sql_pool).await?;
Ok(())
}
async fn start_password_reset(&self, user: &UserId) -> Result<Option<String>> {
let query = Query::select()
let (query, values) = Query::select()
.column(Users::UserId)
.from(Users::Table)
.and_where(Expr::col(Users::UserId).eq(user))
.to_string(DbQueryBuilder {});
.build_sqlx(DbQueryBuilder {});
// Check that the user exists.
if sqlx::query(&query).fetch_one(&self.sql_pool).await.is_err() {
if query_with(&query, values)
.fetch_one(&self.sql_pool)
.await
.is_err()
{
return Ok(None);
}
let token = gen_random_string(100);
let duration = chrono::Duration::minutes(10);
let query = Query::insert()
let (query, values) = Query::insert()
.into_table(PasswordResetTokens::Table)
.columns(vec![
PasswordResetTokens::Token,
@@ -133,31 +138,33 @@ impl TcpBackendHandler for SqlBackendHandler {
user.into(),
(chrono::Utc::now() + duration).naive_utc().into(),
])
.to_string(DbQueryBuilder {});
sqlx::query(&query).execute(&self.sql_pool).await?;
.build_sqlx(DbQueryBuilder {});
query_with(&query, values).execute(&self.sql_pool).await?;
Ok(Some(token))
}
async fn get_user_id_for_password_reset_token(&self, token: &str) -> Result<UserId> {
let query = Query::select()
let (query, values) = Query::select()
.column(PasswordResetTokens::UserId)
.from(PasswordResetTokens::Table)
.and_where(Expr::col(PasswordResetTokens::Token).eq(token))
.and_where(
Expr::col(PasswordResetTokens::ExpiryDate).gt(chrono::Utc::now().naive_utc()),
)
.to_string(DbQueryBuilder {});
.build_sqlx(DbQueryBuilder {});
let (user_id,) = sqlx::query_as(&query).fetch_one(&self.sql_pool).await?;
let (user_id,) = query_as_with(&query, values)
.fetch_one(&self.sql_pool)
.await?;
Ok(user_id)
}
async fn delete_password_reset_token(&self, token: &str) -> Result<()> {
let query = Query::delete()
let (query, values) = Query::delete()
.from_table(PasswordResetTokens::Table)
.and_where(Expr::col(PasswordResetTokens::Token).eq(token))
.to_string(DbQueryBuilder {});
sqlx::query(&query).execute(&self.sql_pool).await?;
.build_sqlx(DbQueryBuilder {});
query_with(&query, values).execute(&self.sql_pool).await?;
Ok(())
}
}