mirror of
https://github.com/storytold/artcraft.git
synced 2026-11-04 01:18:34 +00:00
Successfully move one "user" endpoint into new package
This commit is contained in:
1 parent
273b5f3c4e
commit
d047fa50a9
18 files changed
+417
-348
No files matched your search
Generated
+3
@@ -4944,8 +4944,10 @@ dependencies = [
|
||||
"base64 0.13.0",
|
||||
"chrono",
|
||||
"container_common",
|
||||
"database_queries",
|
||||
"dotenv",
|
||||
"hmac 0.10.1",
|
||||
"http_server_common",
|
||||
"jwt",
|
||||
"log",
|
||||
"once_cell",
|
||||
@@ -4954,6 +4956,7 @@ dependencies = [
|
||||
"serde_derive",
|
||||
"serde_json",
|
||||
"sha2",
|
||||
"sqlx",
|
||||
"time 0.2.26",
|
||||
"toml",
|
||||
]
|
||||
|
||||
@@ -16,6 +16,8 @@ path = "src/lib.rs"
|
||||
|
||||
# Internal
|
||||
container_common = { path = "../../lib/container_common" }
|
||||
database_queries = { path = "../../lib/database_queries" }
|
||||
http_server_common = { path = "../../lib/http_server_common" }
|
||||
|
||||
# External
|
||||
anyhow = "1.0.40"
|
||||
@@ -40,3 +42,9 @@ serde = "1.0.125"
|
||||
serde_derive = "1.0.125"
|
||||
serde_json = "1.0.64"
|
||||
toml = "0.5.8"
|
||||
|
||||
# MySql Database
|
||||
# "offline" supports typesafe builds without a live database
|
||||
# "rustls" doesn't appear to work with IP addresses, per:
|
||||
# https://github.com/launchbadge/sqlx/issues/1095#issuecomment-798050828
|
||||
sqlx = { version = "0.5", features = [ "mysql", "runtime-actix-native-tls", "offline", "chrono" ] }
|
||||
@@ -0,0 +1 @@
|
||||
pub mod session_info;
|
||||
+11
-10
@@ -1,16 +1,16 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
//#![forbid(unused_imports)]
|
||||
//#![forbid(unused_mut)]
|
||||
//#![forbid(unused_variables)]
|
||||
|
||||
use actix_web::error::ResponseError;
|
||||
use actix_web::http::StatusCode;
|
||||
use actix_web::{web, HttpResponse, HttpRequest};
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::server_state::ServerState;
|
||||
use crate::utils::session_checker::SessionChecker;
|
||||
use http_server_common::response::response_error_helpers::to_simple_json_error;
|
||||
use log::warn;
|
||||
use sqlx::MySqlPool;
|
||||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[derive(Serialize, Copy, Clone)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
@@ -104,17 +104,18 @@ impl fmt::Display for SessionInfoError {
|
||||
|
||||
pub async fn session_info_handler(
|
||||
http_request: HttpRequest,
|
||||
server_state: web::Data<Arc<ServerState>>) -> Result<HttpResponse, SessionInfoError>
|
||||
mysql_pool: web::Data<MySqlPool>,
|
||||
session_checker: web::Data<SessionChecker>,
|
||||
) -> Result<HttpResponse, SessionInfoError>
|
||||
{
|
||||
let mut mysql_connection = server_state.mysql_pool.acquire()
|
||||
let mut mysql_connection = mysql_pool.acquire()
|
||||
.await
|
||||
.map_err(|e| {
|
||||
warn!("Could not acquire DB pool: {:?}", e);
|
||||
SessionInfoError::ServerError
|
||||
})?;
|
||||
|
||||
let maybe_user_session = server_state
|
||||
.session_checker
|
||||
let maybe_user_session = session_checker
|
||||
.maybe_get_user_session_from_connection(&http_request, &mut mysql_connection)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
@@ -1 +1,4 @@
|
||||
#[macro_use] extern crate serde_derive;
|
||||
|
||||
pub mod endpoints;
|
||||
pub mod utils;
|
||||
@@ -1 +1,2 @@
|
||||
pub mod session_checker;
|
||||
pub mod session_cookie_manager;
|
||||
@@ -0,0 +1,78 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use actix_web::HttpRequest;
|
||||
use container_common::anyhow_result::AnyhowResult;
|
||||
use crate::utils::session_cookie_manager::SessionCookieManager;
|
||||
use database_queries::queries::users::user_sessions::get_session_by_token::{get_session_by_token, SessionUserRecord};
|
||||
use database_queries::queries::users::user_sessions::get_session_by_token_light::{get_session_by_token_light, SessionRecord};
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{MySqlPool, MySql};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionChecker {
|
||||
cookie_manager: SessionCookieManager,
|
||||
}
|
||||
|
||||
impl SessionChecker {
|
||||
|
||||
pub fn new(cookie_manager: &SessionCookieManager) -> Self {
|
||||
Self {
|
||||
cookie_manager: cookie_manager.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated = "Use the PoolConnection<MySql> method instead of the MySqlPool one."]
|
||||
pub async fn maybe_get_session(
|
||||
&self,
|
||||
request: &HttpRequest,
|
||||
pool: &MySqlPool
|
||||
) -> AnyhowResult<Option<SessionRecord>>
|
||||
{
|
||||
let mut connection = pool.acquire().await?;
|
||||
self.maybe_get_session_from_connection(request, &mut connection).await
|
||||
}
|
||||
|
||||
pub async fn maybe_get_session_from_connection(
|
||||
&self,
|
||||
request: &HttpRequest,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> AnyhowResult<Option<SessionRecord>>
|
||||
{
|
||||
let session_token = match self.cookie_manager.decode_session_token_from_request(request)? {
|
||||
None => return Ok(None),
|
||||
Some(session_token) => session_token,
|
||||
};
|
||||
|
||||
get_session_by_token_light(mysql_connection, &session_token).await
|
||||
}
|
||||
|
||||
|
||||
#[deprecated = "Use the PoolConnection<MySql> method instead of the MySqlPool one."]
|
||||
pub async fn maybe_get_user_session(
|
||||
&self,
|
||||
request: &HttpRequest,
|
||||
pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<SessionUserRecord>>
|
||||
{
|
||||
let mut connection = pool.acquire().await?;
|
||||
self.maybe_get_user_session_from_connection(request, &mut connection).await
|
||||
}
|
||||
|
||||
pub async fn maybe_get_user_session_from_connection(
|
||||
&self,
|
||||
request: &HttpRequest,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> AnyhowResult<Option<SessionUserRecord>>
|
||||
{
|
||||
let session_token = match self.cookie_manager.decode_session_token_from_request(request)? {
|
||||
None => return Ok(None),
|
||||
Some(session_token) => session_token,
|
||||
};
|
||||
|
||||
get_session_by_token(mysql_connection, &session_token).await
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,16 @@ pub fn nullable_i8_to_bool(value: Option<i8>, default_value: bool) -> bool {
|
||||
.unwrap_or(default_value)
|
||||
}
|
||||
|
||||
pub fn nullable_i8_to_bool_default_false(value: Option<i8>) -> bool {
|
||||
match value {
|
||||
None => false,
|
||||
Some(v) => match v {
|
||||
0 => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Bool conversion, but retain nulls.
|
||||
pub fn nullable_i8_to_optional_bool(value: Option<i8>) -> Option<bool> {
|
||||
value.map(|v| i8_to_bool(v))
|
||||
@@ -22,11 +32,7 @@ pub fn nullable_i8_to_optional_bool(value: Option<i8>) -> Option<bool> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::helpers::boolean_converters::{
|
||||
i8_to_bool,
|
||||
nullable_i8_to_bool,
|
||||
nullable_i8_to_optional_bool
|
||||
};
|
||||
use crate::helpers::boolean_converters::{i8_to_bool, nullable_i8_to_bool, nullable_i8_to_bool_default_false, nullable_i8_to_optional_bool};
|
||||
|
||||
#[test]
|
||||
fn test_i8_to_bool() {
|
||||
@@ -52,6 +58,16 @@ mod tests {
|
||||
assert_eq!(nullable_i8_to_bool(Some(-100), true), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nullable_i8_to_bool_default_false() {
|
||||
assert_eq!(nullable_i8_to_bool_default_false(None), false);
|
||||
assert_eq!(nullable_i8_to_bool_default_false(Some(0)), false);
|
||||
|
||||
assert_eq!(nullable_i8_to_bool_default_false(Some(1)), true);
|
||||
assert_eq!(nullable_i8_to_bool_default_false(Some(100)), true);
|
||||
assert_eq!(nullable_i8_to_bool_default_false(Some(-100)), true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nullable_i8_to_optional_bool() {
|
||||
assert_eq!(nullable_i8_to_optional_bool(None), None);
|
||||
|
||||
@@ -0,0 +1,231 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
|
||||
use anyhow::anyhow;
|
||||
use container_common::anyhow_result::AnyhowResult;
|
||||
use crate::column_types::record_visibility::RecordVisibility;
|
||||
use crate::helpers::boolean_converters::{nullable_i8_to_optional_bool, i8_to_bool, nullable_i8_to_bool_default_false};
|
||||
use log::warn;
|
||||
use sqlx::MySql;
|
||||
use sqlx::pool::PoolConnection;
|
||||
|
||||
pub struct SessionUserRecord {
|
||||
pub user_token: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
|
||||
pub email_address: String,
|
||||
pub email_confirmed: bool,
|
||||
pub email_gravatar_hash: String,
|
||||
|
||||
// ===== PREFERENCES ===== //
|
||||
|
||||
pub disable_gravatar: bool,
|
||||
pub auto_play_audio_preference: Option<bool>,
|
||||
pub preferred_tts_result_visibility: RecordVisibility,
|
||||
pub preferred_w2l_result_visibility: RecordVisibility,
|
||||
pub auto_play_video_preference: Option<bool>,
|
||||
|
||||
// ===== ROLE ===== //
|
||||
|
||||
pub user_role_slug: String,
|
||||
pub is_banned: bool,
|
||||
|
||||
// ===== PERMISSIONS FLAGS ===== //
|
||||
|
||||
// Usage
|
||||
pub can_use_tts: bool,
|
||||
pub can_use_w2l: bool,
|
||||
pub can_delete_own_tts_results: bool,
|
||||
pub can_delete_own_w2l_results: bool,
|
||||
pub can_delete_own_account: bool,
|
||||
|
||||
// Contribution
|
||||
pub can_upload_tts_models: bool,
|
||||
pub can_upload_w2l_templates: bool,
|
||||
pub can_delete_own_tts_models: bool,
|
||||
pub can_delete_own_w2l_templates: bool,
|
||||
|
||||
// Moderation
|
||||
pub can_approve_w2l_templates: bool,
|
||||
pub can_edit_other_users_profiles: bool,
|
||||
pub can_edit_other_users_tts_models: bool,
|
||||
pub can_edit_other_users_w2l_templates: bool,
|
||||
pub can_delete_other_users_tts_models: bool,
|
||||
pub can_delete_other_users_tts_results: bool,
|
||||
pub can_delete_other_users_w2l_templates: bool,
|
||||
pub can_delete_other_users_w2l_results: bool,
|
||||
pub can_ban_users: bool,
|
||||
pub can_delete_users: bool,
|
||||
}
|
||||
|
||||
pub async fn get_session_by_token(
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
session_token: &str,
|
||||
) -> AnyhowResult<Option<SessionUserRecord>> {
|
||||
|
||||
// NB: Lookup failure is Err(RowNotFound).
|
||||
let maybe_user_record = sqlx::query_as!(
|
||||
SessionUserRawDbRecord,
|
||||
r#"
|
||||
SELECT
|
||||
users.token as user_token,
|
||||
users.username,
|
||||
users.display_name,
|
||||
|
||||
users.email_address,
|
||||
users.email_confirmed,
|
||||
users.email_gravatar_hash,
|
||||
|
||||
users.disable_gravatar,
|
||||
users.auto_play_audio_preference,
|
||||
users.auto_play_video_preference,
|
||||
users.preferred_tts_result_visibility as `preferred_tts_result_visibility: crate::column_types::record_visibility::RecordVisibility`,
|
||||
users.preferred_w2l_result_visibility as `preferred_w2l_result_visibility: crate::column_types::record_visibility::RecordVisibility`,
|
||||
|
||||
users.user_role_slug,
|
||||
users.is_banned,
|
||||
|
||||
user_roles.can_use_tts,
|
||||
user_roles.can_use_w2l,
|
||||
user_roles.can_delete_own_tts_results,
|
||||
user_roles.can_delete_own_w2l_results,
|
||||
user_roles.can_delete_own_account,
|
||||
|
||||
user_roles.can_upload_tts_models,
|
||||
user_roles.can_upload_w2l_templates,
|
||||
user_roles.can_delete_own_tts_models,
|
||||
user_roles.can_delete_own_w2l_templates,
|
||||
|
||||
user_roles.can_approve_w2l_templates,
|
||||
user_roles.can_edit_other_users_profiles,
|
||||
user_roles.can_edit_other_users_tts_models,
|
||||
user_roles.can_edit_other_users_w2l_templates,
|
||||
user_roles.can_delete_other_users_tts_models,
|
||||
user_roles.can_delete_other_users_tts_results,
|
||||
user_roles.can_delete_other_users_w2l_templates,
|
||||
user_roles.can_delete_other_users_w2l_results,
|
||||
user_roles.can_ban_users,
|
||||
user_roles.can_delete_users
|
||||
|
||||
FROM users
|
||||
LEFT OUTER JOIN user_sessions
|
||||
ON users.token = user_sessions.user_token
|
||||
LEFT OUTER JOIN user_roles
|
||||
ON users.user_role_slug = user_roles.slug
|
||||
WHERE user_sessions.token = ?
|
||||
AND user_sessions.deleted_at IS NULL
|
||||
AND users.user_deleted_at IS NULL
|
||||
AND users.mod_deleted_at IS NULL
|
||||
"#,
|
||||
session_token,
|
||||
)
|
||||
.fetch_one(mysql_connection)
|
||||
.await; // TODO: This will return error if it doesn't exist
|
||||
|
||||
match maybe_user_record {
|
||||
Ok(raw_user_record) => {
|
||||
let result_user_record = SessionUserRecord {
|
||||
user_token: raw_user_record.user_token,
|
||||
username: raw_user_record.username,
|
||||
display_name: raw_user_record.display_name,
|
||||
email_address: raw_user_record.email_address,
|
||||
email_confirmed: i8_to_bool(raw_user_record.email_confirmed),
|
||||
email_gravatar_hash: raw_user_record.email_gravatar_hash,
|
||||
// Preference
|
||||
disable_gravatar: i8_to_bool(raw_user_record.disable_gravatar),
|
||||
auto_play_audio_preference: nullable_i8_to_optional_bool(raw_user_record.auto_play_audio_preference),
|
||||
auto_play_video_preference: nullable_i8_to_optional_bool(raw_user_record.auto_play_video_preference),
|
||||
user_role_slug: raw_user_record.user_role_slug,
|
||||
preferred_tts_result_visibility: raw_user_record.preferred_tts_result_visibility,
|
||||
preferred_w2l_result_visibility: raw_user_record.preferred_w2l_result_visibility,
|
||||
|
||||
is_banned: i8_to_bool(raw_user_record.is_banned),
|
||||
|
||||
// Usage
|
||||
can_use_tts: nullable_i8_to_bool_default_false(raw_user_record.can_use_tts),
|
||||
can_use_w2l: nullable_i8_to_bool_default_false(raw_user_record.can_use_w2l),
|
||||
can_delete_own_tts_results: nullable_i8_to_bool_default_false(raw_user_record.can_delete_own_tts_results),
|
||||
can_delete_own_w2l_results: nullable_i8_to_bool_default_false(raw_user_record.can_delete_own_w2l_results),
|
||||
can_delete_own_account: nullable_i8_to_bool_default_false(raw_user_record.can_delete_own_account),
|
||||
// Contribution
|
||||
can_upload_tts_models: nullable_i8_to_bool_default_false(raw_user_record.can_upload_tts_models),
|
||||
can_upload_w2l_templates: nullable_i8_to_bool_default_false(raw_user_record.can_upload_w2l_templates),
|
||||
can_delete_own_tts_models: nullable_i8_to_bool_default_false(raw_user_record.can_delete_own_tts_models),
|
||||
can_delete_own_w2l_templates: nullable_i8_to_bool_default_false(raw_user_record.can_delete_own_w2l_templates),
|
||||
// Moderation
|
||||
can_approve_w2l_templates: nullable_i8_to_bool_default_false(raw_user_record.can_approve_w2l_templates),
|
||||
can_edit_other_users_profiles: nullable_i8_to_bool_default_false(raw_user_record.can_edit_other_users_profiles),
|
||||
can_edit_other_users_tts_models: nullable_i8_to_bool_default_false(raw_user_record.can_edit_other_users_tts_models),
|
||||
can_edit_other_users_w2l_templates: nullable_i8_to_bool_default_false(raw_user_record.can_edit_other_users_w2l_templates),
|
||||
can_delete_other_users_tts_models: nullable_i8_to_bool_default_false(raw_user_record.can_delete_other_users_tts_models),
|
||||
can_delete_other_users_tts_results: nullable_i8_to_bool_default_false(raw_user_record.can_delete_other_users_tts_results),
|
||||
can_delete_other_users_w2l_templates: nullable_i8_to_bool_default_false(raw_user_record.can_delete_other_users_w2l_templates),
|
||||
can_delete_other_users_w2l_results: nullable_i8_to_bool_default_false(raw_user_record.can_delete_other_users_w2l_results),
|
||||
can_ban_users: nullable_i8_to_bool_default_false(raw_user_record.can_ban_users),
|
||||
can_delete_users: nullable_i8_to_bool_default_false(raw_user_record.can_delete_users) ,
|
||||
};
|
||||
|
||||
Ok(Some(result_user_record))
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
RowNotFound => {
|
||||
warn!("Valid cookie; invalid session: {}", session_token);
|
||||
Ok(None)
|
||||
},
|
||||
_ => {
|
||||
warn!("Session query error: {:?}", err);
|
||||
Err(anyhow!("session query error: {:?}", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
struct SessionUserRawDbRecord {
|
||||
user_token: String,
|
||||
username: String,
|
||||
display_name: String,
|
||||
|
||||
email_address: String,
|
||||
email_confirmed: i8,
|
||||
email_gravatar_hash: String,
|
||||
|
||||
disable_gravatar: i8,
|
||||
auto_play_audio_preference: Option<i8>,
|
||||
auto_play_video_preference: Option<i8>,
|
||||
preferred_tts_result_visibility: RecordVisibility,
|
||||
preferred_w2l_result_visibility: RecordVisibility,
|
||||
|
||||
user_role_slug: String,
|
||||
is_banned: i8,
|
||||
|
||||
// NB: These are `Option` due to the JOIN not being compile-time assured.
|
||||
// Usage
|
||||
can_use_tts: Option<i8>,
|
||||
can_use_w2l: Option<i8>,
|
||||
can_delete_own_tts_results: Option<i8>,
|
||||
can_delete_own_w2l_results: Option<i8>,
|
||||
can_delete_own_account: Option<i8>,
|
||||
|
||||
// Contribution
|
||||
can_upload_tts_models: Option<i8>,
|
||||
can_upload_w2l_templates: Option<i8>,
|
||||
can_delete_own_tts_models: Option<i8>,
|
||||
can_delete_own_w2l_templates: Option<i8>,
|
||||
|
||||
// Moderation
|
||||
can_approve_w2l_templates: Option<i8>,
|
||||
can_edit_other_users_profiles: Option<i8>,
|
||||
can_edit_other_users_tts_models: Option<i8>,
|
||||
can_edit_other_users_w2l_templates: Option<i8>,
|
||||
can_delete_other_users_tts_models: Option<i8>,
|
||||
can_delete_other_users_tts_results: Option<i8>,
|
||||
can_delete_other_users_w2l_templates: Option<i8>,
|
||||
can_delete_other_users_w2l_results: Option<i8>,
|
||||
can_ban_users: Option<i8>,
|
||||
can_delete_users: Option<i8>,
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
|
||||
use anyhow::anyhow;
|
||||
use container_common::anyhow_result::AnyhowResult;
|
||||
use log::warn;
|
||||
use sqlx::MySql;
|
||||
use sqlx::pool::PoolConnection;
|
||||
|
||||
pub struct SessionRecord {
|
||||
pub session_token: String,
|
||||
pub user_token: String,
|
||||
}
|
||||
pub async fn get_session_by_token_light(
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
session_token: &str,
|
||||
) -> AnyhowResult<Option<SessionRecord>> {
|
||||
|
||||
// NB: Lookup failure is Err(RowNotFound).
|
||||
let maybe_session_record = sqlx::query_as!(
|
||||
SessionRecord,
|
||||
r#"
|
||||
SELECT
|
||||
token as session_token,
|
||||
user_token
|
||||
FROM user_sessions
|
||||
WHERE token = ?
|
||||
AND deleted_at IS NULL
|
||||
"#,
|
||||
session_token,
|
||||
)
|
||||
.fetch_one(mysql_connection)
|
||||
.await;
|
||||
|
||||
match maybe_session_record {
|
||||
Ok(session_record) => Ok(Some(session_record)),
|
||||
Err(err) => {
|
||||
match err {
|
||||
RowNotFound => {
|
||||
warn!("Valid cookie; invalid session: {}", session_token);
|
||||
Ok(None)
|
||||
},
|
||||
_ => {
|
||||
warn!("Session query error: {:?}", err);
|
||||
Err(anyhow!("session query error: {:?}", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1,3 @@
|
||||
pub mod create_session;
|
||||
pub mod create_session;
|
||||
pub mod get_session_by_token;
|
||||
pub mod get_session_by_token_light;
|
||||
@@ -10,7 +10,6 @@ use chrono::{DateTime, Utc};
|
||||
use crate::AnyhowResult;
|
||||
use crate::http_server::web_utils::ip_address::get_request_ip;
|
||||
use crate::http_server::web_utils::response_error_helpers::to_simple_json_error;
|
||||
use crate::http_server::web_utils::session_checker::SessionRecord;
|
||||
use crate::server_state::ServerState;
|
||||
use crate::validations::passwords::validate_passwords;
|
||||
use crate::validations::username::validate_username;
|
||||
|
||||
@@ -8,4 +8,3 @@ pub mod list_user_w2l_inference_results;
|
||||
pub mod list_user_w2l_templates;
|
||||
pub mod login;
|
||||
pub mod logout;
|
||||
pub mod session_info;
|
||||
@@ -4,5 +4,4 @@ pub mod read_multipart_field_bytes;
|
||||
pub mod redis_rate_limiter;
|
||||
pub mod response_error_helpers;
|
||||
pub mod response_success_helpers;
|
||||
pub mod serialize_as_json_error;
|
||||
pub mod session_checker;
|
||||
pub mod serialize_as_json_error;
|
||||
@@ -1,324 +0,0 @@
|
||||
// NB: Incrementally getting rid of build warnings...
|
||||
#![forbid(unused_imports)]
|
||||
#![forbid(unused_mut)]
|
||||
#![forbid(unused_variables)]
|
||||
|
||||
use actix_web::HttpRequest;
|
||||
use anyhow::anyhow;
|
||||
use crate::AnyhowResult;
|
||||
use database_queries::column_types::record_visibility::RecordVisibility;
|
||||
use database_queries::helpers::boolean_converters::{nullable_i8_to_optional_bool, i8_to_bool};
|
||||
use log::warn;
|
||||
use sqlx::error::Error::RowNotFound;
|
||||
use sqlx::pool::PoolConnection;
|
||||
use sqlx::{MySqlPool, MySql};
|
||||
use users_component::utils::session_cookie_manager::SessionCookieManager;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SessionChecker {
|
||||
cookie_manager: SessionCookieManager,
|
||||
}
|
||||
|
||||
pub struct SessionRecord {
|
||||
pub session_token: String,
|
||||
pub user_token: String,
|
||||
}
|
||||
|
||||
pub struct SessionUserRecord {
|
||||
pub user_token: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
|
||||
pub email_address: String,
|
||||
pub email_confirmed: bool,
|
||||
pub email_gravatar_hash: String,
|
||||
|
||||
// ===== PREFERENCES ===== //
|
||||
|
||||
pub disable_gravatar: bool,
|
||||
pub auto_play_audio_preference: Option<bool>,
|
||||
pub preferred_tts_result_visibility: RecordVisibility,
|
||||
pub preferred_w2l_result_visibility: RecordVisibility,
|
||||
pub auto_play_video_preference: Option<bool>,
|
||||
|
||||
// ===== ROLE ===== //
|
||||
|
||||
pub user_role_slug: String,
|
||||
pub is_banned: bool,
|
||||
|
||||
// ===== PERMISSIONS FLAGS ===== //
|
||||
|
||||
// Usage
|
||||
pub can_use_tts: bool,
|
||||
pub can_use_w2l: bool,
|
||||
pub can_delete_own_tts_results: bool,
|
||||
pub can_delete_own_w2l_results: bool,
|
||||
pub can_delete_own_account: bool,
|
||||
|
||||
// Contribution
|
||||
pub can_upload_tts_models: bool,
|
||||
pub can_upload_w2l_templates: bool,
|
||||
pub can_delete_own_tts_models: bool,
|
||||
pub can_delete_own_w2l_templates: bool,
|
||||
|
||||
// Moderation
|
||||
pub can_approve_w2l_templates: bool,
|
||||
pub can_edit_other_users_profiles: bool,
|
||||
pub can_edit_other_users_tts_models: bool,
|
||||
pub can_edit_other_users_w2l_templates: bool,
|
||||
pub can_delete_other_users_tts_models: bool,
|
||||
pub can_delete_other_users_tts_results: bool,
|
||||
pub can_delete_other_users_w2l_templates: bool,
|
||||
pub can_delete_other_users_w2l_results: bool,
|
||||
pub can_ban_users: bool,
|
||||
pub can_delete_users: bool,
|
||||
}
|
||||
|
||||
pub struct SessionUserRawDbRecord {
|
||||
pub user_token: String,
|
||||
pub username: String,
|
||||
pub display_name: String,
|
||||
|
||||
pub email_address: String,
|
||||
pub email_confirmed: i8,
|
||||
pub email_gravatar_hash: String,
|
||||
|
||||
pub disable_gravatar: i8,
|
||||
pub auto_play_audio_preference: Option<i8>,
|
||||
pub auto_play_video_preference: Option<i8>,
|
||||
pub preferred_tts_result_visibility: RecordVisibility,
|
||||
pub preferred_w2l_result_visibility: RecordVisibility,
|
||||
|
||||
pub user_role_slug: String,
|
||||
pub is_banned: i8,
|
||||
|
||||
// NB: These are `Option` due to the JOIN not being compile-time assured.
|
||||
// Usage
|
||||
pub can_use_tts: Option<i8>,
|
||||
pub can_use_w2l: Option<i8>,
|
||||
pub can_delete_own_tts_results: Option<i8>,
|
||||
pub can_delete_own_w2l_results: Option<i8>,
|
||||
pub can_delete_own_account: Option<i8>,
|
||||
|
||||
// Contribution
|
||||
pub can_upload_tts_models: Option<i8>,
|
||||
pub can_upload_w2l_templates: Option<i8>,
|
||||
pub can_delete_own_tts_models: Option<i8>,
|
||||
pub can_delete_own_w2l_templates: Option<i8>,
|
||||
|
||||
// Moderation
|
||||
pub can_approve_w2l_templates: Option<i8>,
|
||||
pub can_edit_other_users_profiles: Option<i8>,
|
||||
pub can_edit_other_users_tts_models: Option<i8>,
|
||||
pub can_edit_other_users_w2l_templates: Option<i8>,
|
||||
pub can_delete_other_users_tts_models: Option<i8>,
|
||||
pub can_delete_other_users_tts_results: Option<i8>,
|
||||
pub can_delete_other_users_w2l_templates: Option<i8>,
|
||||
pub can_delete_other_users_w2l_results: Option<i8>,
|
||||
pub can_ban_users: Option<i8>,
|
||||
pub can_delete_users: Option<i8>,
|
||||
}
|
||||
|
||||
impl SessionChecker {
|
||||
|
||||
pub fn new(cookie_manager: &SessionCookieManager) -> Self {
|
||||
Self {
|
||||
cookie_manager: cookie_manager.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn maybe_get_session(&self, request: &HttpRequest, pool: &MySqlPool)
|
||||
-> AnyhowResult<Option<SessionRecord>>
|
||||
{
|
||||
let session_token = match self.cookie_manager.decode_session_token_from_request(request)? {
|
||||
None => return Ok(None),
|
||||
Some(session_token) => session_token,
|
||||
};
|
||||
|
||||
// NB: Lookup failure is Err(RowNotFound).
|
||||
let maybe_session_record = sqlx::query_as!(
|
||||
SessionRecord,
|
||||
r#"
|
||||
SELECT
|
||||
token as session_token,
|
||||
user_token
|
||||
FROM user_sessions
|
||||
WHERE token = ?
|
||||
AND deleted_at IS NULL
|
||||
"#,
|
||||
session_token.to_string(),
|
||||
)
|
||||
.fetch_one(pool)
|
||||
.await; // TODO: This will return error if it doesn't exist
|
||||
|
||||
match maybe_session_record {
|
||||
Ok(session_record) => {
|
||||
Ok(Some(session_record))
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
RowNotFound => {
|
||||
warn!("Valid cookie; invalid session: {}", session_token);
|
||||
Ok(None)
|
||||
},
|
||||
_ => {
|
||||
warn!("Session query error: {:?}", err);
|
||||
Err(anyhow!("session query error: {:?}", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[deprecated = "Use the PoolConnection<MySql> method instead of the MySqlPool one."]
|
||||
pub async fn maybe_get_user_session(
|
||||
&self,
|
||||
request: &HttpRequest,
|
||||
pool: &MySqlPool,
|
||||
) -> AnyhowResult<Option<SessionUserRecord>>
|
||||
{
|
||||
let mut connection = pool.acquire().await?;
|
||||
self.maybe_get_user_session_from_connection(request, &mut connection).await
|
||||
}
|
||||
|
||||
pub async fn maybe_get_user_session_from_connection(
|
||||
&self,
|
||||
request: &HttpRequest,
|
||||
mysql_connection: &mut PoolConnection<MySql>,
|
||||
) -> AnyhowResult<Option<SessionUserRecord>>
|
||||
{
|
||||
|
||||
let session_token = match self.cookie_manager.decode_session_token_from_request(request)? {
|
||||
None => return Ok(None),
|
||||
Some(session_token) => session_token,
|
||||
};
|
||||
|
||||
// NB: Lookup failure is Err(RowNotFound).
|
||||
let maybe_user_record = sqlx::query_as!(
|
||||
SessionUserRawDbRecord,
|
||||
r#"
|
||||
SELECT
|
||||
users.token as user_token,
|
||||
users.username,
|
||||
users.display_name,
|
||||
|
||||
users.email_address,
|
||||
users.email_confirmed,
|
||||
users.email_gravatar_hash,
|
||||
|
||||
users.disable_gravatar,
|
||||
users.auto_play_audio_preference,
|
||||
users.auto_play_video_preference,
|
||||
users.preferred_tts_result_visibility as `preferred_tts_result_visibility: database_queries::column_types::record_visibility::RecordVisibility`,
|
||||
users.preferred_w2l_result_visibility as `preferred_w2l_result_visibility: database_queries::column_types::record_visibility::RecordVisibility`,
|
||||
|
||||
users.user_role_slug,
|
||||
users.is_banned,
|
||||
|
||||
user_roles.can_use_tts,
|
||||
user_roles.can_use_w2l,
|
||||
user_roles.can_delete_own_tts_results,
|
||||
user_roles.can_delete_own_w2l_results,
|
||||
user_roles.can_delete_own_account,
|
||||
|
||||
user_roles.can_upload_tts_models,
|
||||
user_roles.can_upload_w2l_templates,
|
||||
user_roles.can_delete_own_tts_models,
|
||||
user_roles.can_delete_own_w2l_templates,
|
||||
|
||||
user_roles.can_approve_w2l_templates,
|
||||
user_roles.can_edit_other_users_profiles,
|
||||
user_roles.can_edit_other_users_tts_models,
|
||||
user_roles.can_edit_other_users_w2l_templates,
|
||||
user_roles.can_delete_other_users_tts_models,
|
||||
user_roles.can_delete_other_users_tts_results,
|
||||
user_roles.can_delete_other_users_w2l_templates,
|
||||
user_roles.can_delete_other_users_w2l_results,
|
||||
user_roles.can_ban_users,
|
||||
user_roles.can_delete_users
|
||||
|
||||
FROM users
|
||||
LEFT OUTER JOIN user_sessions
|
||||
ON users.token = user_sessions.user_token
|
||||
LEFT OUTER JOIN user_roles
|
||||
ON users.user_role_slug = user_roles.slug
|
||||
WHERE user_sessions.token = ?
|
||||
AND user_sessions.deleted_at IS NULL
|
||||
AND users.user_deleted_at IS NULL
|
||||
AND users.mod_deleted_at IS NULL
|
||||
"#,
|
||||
session_token.to_string(),
|
||||
)
|
||||
.fetch_one(mysql_connection)
|
||||
.await; // TODO: This will return error if it doesn't exist
|
||||
|
||||
match maybe_user_record {
|
||||
Ok(raw_user_record) => {
|
||||
let result_user_record = SessionUserRecord {
|
||||
user_token: raw_user_record.user_token,
|
||||
username: raw_user_record.username,
|
||||
display_name: raw_user_record.display_name,
|
||||
email_address: raw_user_record.email_address,
|
||||
email_confirmed: i8_to_bool(raw_user_record.email_confirmed),
|
||||
email_gravatar_hash: raw_user_record.email_gravatar_hash,
|
||||
// Preference
|
||||
disable_gravatar: i8_to_bool(raw_user_record.disable_gravatar),
|
||||
auto_play_audio_preference: nullable_i8_to_optional_bool(raw_user_record.auto_play_audio_preference),
|
||||
auto_play_video_preference: nullable_i8_to_optional_bool(raw_user_record.auto_play_video_preference),
|
||||
user_role_slug: raw_user_record.user_role_slug,
|
||||
preferred_tts_result_visibility: raw_user_record.preferred_tts_result_visibility,
|
||||
preferred_w2l_result_visibility: raw_user_record.preferred_w2l_result_visibility,
|
||||
|
||||
is_banned: i8_to_bool(raw_user_record.is_banned),
|
||||
|
||||
// Usage
|
||||
can_use_tts: convert_optional_db_bool_default_false(raw_user_record.can_use_tts),
|
||||
can_use_w2l: convert_optional_db_bool_default_false(raw_user_record.can_use_w2l),
|
||||
can_delete_own_tts_results: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_tts_results),
|
||||
can_delete_own_w2l_results: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_w2l_results),
|
||||
can_delete_own_account: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_account),
|
||||
// Contribution
|
||||
can_upload_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_upload_tts_models),
|
||||
can_upload_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_upload_w2l_templates),
|
||||
can_delete_own_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_tts_models),
|
||||
can_delete_own_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_delete_own_w2l_templates),
|
||||
// Moderation
|
||||
can_approve_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_approve_w2l_templates),
|
||||
can_edit_other_users_profiles: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_profiles),
|
||||
can_edit_other_users_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_tts_models),
|
||||
can_edit_other_users_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_edit_other_users_w2l_templates),
|
||||
can_delete_other_users_tts_models: convert_optional_db_bool_default_false(raw_user_record.can_delete_other_users_tts_models),
|
||||
can_delete_other_users_tts_results: convert_optional_db_bool_default_false(raw_user_record.can_delete_other_users_tts_results),
|
||||
can_delete_other_users_w2l_templates: convert_optional_db_bool_default_false(raw_user_record.can_delete_other_users_w2l_templates),
|
||||
can_delete_other_users_w2l_results: convert_optional_db_bool_default_false(raw_user_record.can_delete_other_users_w2l_results),
|
||||
can_ban_users: convert_optional_db_bool_default_false(raw_user_record.can_ban_users),
|
||||
can_delete_users: convert_optional_db_bool_default_false(raw_user_record.can_delete_users) ,
|
||||
};
|
||||
|
||||
Ok(Some(result_user_record))
|
||||
},
|
||||
Err(err) => {
|
||||
match err {
|
||||
RowNotFound => {
|
||||
warn!("Valid cookie; invalid session: {}", session_token);
|
||||
Ok(None)
|
||||
},
|
||||
_ => {
|
||||
warn!("Session query error: {:?}", err);
|
||||
Err(anyhow!("session query error: {:?}", err))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn convert_optional_db_bool_default_false(value: Option<i8>) -> bool {
|
||||
match value {
|
||||
None => false,
|
||||
Some(v) => match v {
|
||||
0 => false,
|
||||
_ => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -86,7 +86,6 @@ use crate::http_server::endpoints::users::list_user_w2l_inference_results::list_
|
||||
use crate::http_server::endpoints::users::list_user_w2l_templates::list_user_w2l_templates_handler;
|
||||
use crate::http_server::endpoints::users::login::login_handler;
|
||||
use crate::http_server::endpoints::users::logout::logout_handler;
|
||||
use crate::http_server::endpoints::users::session_info::session_info_handler;
|
||||
use crate::http_server::endpoints::w2l::delete_w2l_result::delete_w2l_inference_result_handler;
|
||||
use crate::http_server::endpoints::w2l::delete_w2l_template::delete_w2l_template_handler;
|
||||
use crate::http_server::endpoints::w2l::edit_w2l_result::edit_w2l_inference_result_handler;
|
||||
@@ -103,7 +102,6 @@ use crate::http_server::endpoints::w2l::list_w2l_templates::list_w2l_templates_h
|
||||
use crate::http_server::endpoints::w2l::set_w2l_template_mod_approval::set_w2l_template_mod_approval_handler;
|
||||
use crate::http_server::middleware::ip_filter_middleware::IpFilter;
|
||||
use crate::http_server::web_utils::redis_rate_limiter::RedisRateLimiter;
|
||||
use crate::http_server::web_utils::session_checker::SessionChecker;
|
||||
use crate::routes::add_routes;
|
||||
use crate::server_state::{ServerState, EnvConfig, TwitchOauthSecrets, TwitchOauth, RedisRateLimiters, InMemoryCaches};
|
||||
use crate::threads::db_health_checker_thread::db_health_check_status::HealthCheckStatus;
|
||||
@@ -128,6 +126,7 @@ use std::time::Duration;
|
||||
use storage_buckets_common::bucket_client::BucketClient;
|
||||
use tokio::runtime::Runtime;
|
||||
use twitch_common::twitch_secrets::TwitchSecrets;
|
||||
use users_component::utils::session_checker::SessionChecker;
|
||||
use users_component::utils::session_cookie_manager::SessionCookieManager;
|
||||
|
||||
// TODO TODO TODO TODO
|
||||
@@ -436,6 +435,8 @@ pub async fn serve(server_state: ServerState) -> AnyhowResult<()>
|
||||
let build_sha = std::fs::read_to_string("/GIT_SHA").unwrap_or(String::from("unknown"));
|
||||
|
||||
let app = App::new()
|
||||
.app_data(web::Data::new(server_state_arc.mysql_pool.clone()))
|
||||
.app_data(web::Data::new(server_state_arc.session_checker.clone()))
|
||||
.app_data(server_state_arc.clone())
|
||||
.wrap(build_common_cors_config())
|
||||
.wrap(DefaultHeaders::new()
|
||||
|
||||
@@ -76,7 +76,6 @@ use crate::http_server::endpoints::users::list_user_w2l_inference_results::list_
|
||||
use crate::http_server::endpoints::users::list_user_w2l_templates::list_user_w2l_templates_handler;
|
||||
use crate::http_server::endpoints::users::login::login_handler;
|
||||
use crate::http_server::endpoints::users::logout::logout_handler;
|
||||
use crate::http_server::endpoints::users::session_info::session_info_handler;
|
||||
use crate::http_server::endpoints::vocoders::get_vocoder::get_vocoder_handler;
|
||||
use crate::http_server::endpoints::vocoders::list_vocoders::list_vocoders_handler;
|
||||
use crate::http_server::endpoints::voice_clone_requests::check_if_voice_clone_request_submitted::check_if_voice_clone_request_submitted_handler;
|
||||
@@ -95,6 +94,7 @@ use crate::http_server::endpoints::w2l::get_w2l_template_use_count::get_w2l_temp
|
||||
use crate::http_server::endpoints::w2l::get_w2l_upload_template_job_status::get_w2l_upload_template_job_status_handler;
|
||||
use crate::http_server::endpoints::w2l::list_w2l_templates::list_w2l_templates_handler;
|
||||
use crate::http_server::endpoints::w2l::set_w2l_template_mod_approval::set_w2l_template_mod_approval_handler;
|
||||
use users_component::endpoints::session_info::session_info_handler;
|
||||
|
||||
pub fn add_routes<T, B> (app: App<T, B>) -> App<T, B>
|
||||
where
|
||||
|
||||
@@ -2,7 +2,6 @@ use crate::StaticApiTokenSet;
|
||||
use crate::http_server::endpoints::categories::list_tts_categories::DisplayCategory;
|
||||
use crate::http_server::endpoints::tts::list_tts_models::TtsModelRecordForResponse;
|
||||
use crate::http_server::web_utils::redis_rate_limiter::RedisRateLimiter;
|
||||
use crate::http_server::web_utils::session_checker::SessionChecker;
|
||||
use crate::threads::db_health_checker_thread::db_health_check_status::HealthCheckStatus;
|
||||
use crate::threads::ip_banlist_set::IpBanlistSet;
|
||||
use crate::util::encrypted_sort_id::SortKeyCrypto;
|
||||
@@ -14,6 +13,7 @@ use memory_caching::single_item_ttl_cache::SingleItemTtlCache;
|
||||
use r2d2_redis::{r2d2, RedisConnectionManager};
|
||||
use sqlx::MySqlPool;
|
||||
use storage_buckets_common::bucket_client::BucketClient;
|
||||
use users_component::utils::session_checker::SessionChecker;
|
||||
use users_component::utils::session_cookie_manager::SessionCookieManager;
|
||||
|
||||
/// State that is injected into every endpoint.
|
||||
|
||||
Reference in new issue
Block a user