From eee75f0711b2e76a9b90fed588207f38ce9bc119 Mon Sep 17 00:00:00 2001 From: Brandon Thomas Date: Thu, 14 Dec 2023 04:37:44 -0500 Subject: [PATCH] polymorphic token checks (thanks kasisnu!) + new token types --- .../by_table/comments/comment_entity_type.rs | 18 ++++++++++ .../user_bookmark_entity_type.rs | 9 +++++ .../src/by_table/user_ratings/entity_type.rs | 18 ++++++++++ .../user_ratings/user_rating_entity.rs | 8 +++++ .../queries/comments/comment_entity_token.rs | 8 +++++ .../user_bookmarks/list_user_bookmarks.rs | 4 +++ .../list_user_bookmarks_result.rs | 2 ++ .../user_bookmark_entity_token.rs | 4 +++ .../comments/create_comment_handler.rs | 27 +++++++++++++++ .../create_user_bookmark_handler.rs | 34 ++++++++++++++++++- .../user_ratings/get_user_rating_handler.rs | 7 +++- .../user_ratings/set_user_rating_handler.rs | 32 +++++++++++++++-- 12 files changed, 167 insertions(+), 4 deletions(-) diff --git a/crates/_types/enums/src/by_table/comments/comment_entity_type.rs b/crates/_types/enums/src/by_table/comments/comment_entity_type.rs index 72294e2b27..014e04e17f 100644 --- a/crates/_types/enums/src/by_table/comments/comment_entity_type.rs +++ b/crates/_types/enums/src/by_table/comments/comment_entity_type.rs @@ -13,6 +13,14 @@ pub enum CommentEntityType { #[serde(rename = "user")] User, + /// Media files + #[serde(rename = "media_file")] + MediaFile, + + /// Model weights + #[serde(rename = "model_weight")] + ModelWeight, + /// TTS model (architecture does not matter) #[serde(rename = "tts_model")] TtsModel, @@ -39,6 +47,8 @@ impl CommentEntityType { pub fn to_str(&self) -> &'static str { match self { Self::User => "user", + Self::MediaFile => "media_file", + Self::ModelWeight => "model_weight", Self::TtsModel => "tts_model", Self::TtsResult => "tts_result", Self::W2lTemplate => "w2l_template", @@ -49,6 +59,8 @@ impl CommentEntityType { pub fn from_str(value: &str) -> Result { match value { "user" => Ok(Self::User), + "media_file" => Ok(Self::MediaFile), + "model_weight" => Ok(Self::ModelWeight), "tts_model" => Ok(Self::TtsModel), "tts_result" => Ok(Self::TtsResult), "w2l_template" => Ok(Self::W2lTemplate), @@ -69,6 +81,8 @@ mod tests { #[test] fn test_serialization() { assert_serialization(CommentEntityType::User, "user"); + assert_serialization(CommentEntityType::MediaFile, "media_file"); + assert_serialization(CommentEntityType::ModelWeight, "model_weight"); assert_serialization(CommentEntityType::TtsModel, "tts_model"); assert_serialization(CommentEntityType::TtsResult, "tts_result"); assert_serialization(CommentEntityType::W2lTemplate, "w2l_template"); @@ -82,6 +96,8 @@ mod tests { #[test] fn test_to_str() { assert_eq!(CommentEntityType::User.to_str(), "user"); + assert_eq!(CommentEntityType::MediaFile.to_str(), "media_file"); + assert_eq!(CommentEntityType::ModelWeight.to_str(), "model_weight"); assert_eq!(CommentEntityType::TtsModel.to_str(), "tts_model"); assert_eq!(CommentEntityType::TtsResult.to_str(), "tts_result"); assert_eq!(CommentEntityType::W2lTemplate.to_str(), "w2l_template"); @@ -91,6 +107,8 @@ mod tests { #[test] fn test_from_str() { assert_eq!(CommentEntityType::from_str("user").unwrap(), CommentEntityType::User); + assert_eq!(CommentEntityType::from_str("media_file").unwrap(), CommentEntityType::MediaFile); + assert_eq!(CommentEntityType::from_str("model_weight").unwrap(), CommentEntityType::ModelWeight); assert_eq!(CommentEntityType::from_str("tts_model").unwrap(), CommentEntityType::TtsModel); assert_eq!(CommentEntityType::from_str("tts_result").unwrap(), CommentEntityType::TtsResult); assert_eq!(CommentEntityType::from_str("w2l_template").unwrap(), CommentEntityType::W2lTemplate); diff --git a/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs b/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs index 69bf47cf5e..1e3fa6692c 100644 --- a/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs +++ b/crates/_types/enums/src/by_table/user_bookmarks/user_bookmark_entity_type.rs @@ -33,6 +33,10 @@ pub enum UserBookmarkEntityType { #[serde(rename = "media_file")] MediaFile, + /// ModelWeight (the new way to store models) + #[serde(rename = "model_weight")] + ModelWeight, + /// VoiceConversionModel #[serde(rename = "voice_conversion_model")] VoiceConversionModel, @@ -56,6 +60,7 @@ impl UserBookmarkEntityType { Self::W2lTemplate => "w2l_template", Self::W2lResult => "w2l_result", Self::MediaFile => "media_file", + Self::ModelWeight => "model_weight", Self::VoiceConversionModel => "voice_conversion_model", Self::ZsVoice => "zs_voice", } @@ -69,6 +74,7 @@ impl UserBookmarkEntityType { "w2l_template" => Ok(Self::W2lTemplate), "w2l_result" => Ok(Self::W2lResult), "media_file" => Ok(Self::MediaFile), + "model_weight" => Ok(Self::ModelWeight), "voice_conversion_model" => Ok(Self::VoiceConversionModel), "zs_voice" => Ok(Self::ZsVoice), _ => Err(format!("invalid value: {:?}", value)), @@ -92,6 +98,7 @@ mod tests { assert_serialization(UserBookmarkEntityType::W2lTemplate, "w2l_template"); assert_serialization(UserBookmarkEntityType::W2lResult, "w2l_result"); assert_serialization(UserBookmarkEntityType::MediaFile, "media_file"); + assert_serialization(UserBookmarkEntityType::ModelWeight, "model_weight"); assert_serialization(UserBookmarkEntityType::VoiceConversionModel, "voice_conversion_model"); assert_serialization(UserBookmarkEntityType::ZsVoice, "zs_voice"); } @@ -108,6 +115,7 @@ mod tests { assert_eq!(UserBookmarkEntityType::W2lTemplate.to_str(), "w2l_template"); assert_eq!(UserBookmarkEntityType::W2lResult.to_str(), "w2l_result"); assert_eq!(UserBookmarkEntityType::MediaFile.to_str(), "media_file"); + assert_eq!(UserBookmarkEntityType::ModelWeight.to_str(), "model_weight"); assert_eq!(UserBookmarkEntityType::VoiceConversionModel.to_str(), "voice_conversion_model"); assert_eq!(UserBookmarkEntityType::ZsVoice.to_str(), "zs_voice"); } @@ -120,6 +128,7 @@ mod tests { assert_eq!(UserBookmarkEntityType::from_str("w2l_template").unwrap(), UserBookmarkEntityType::W2lTemplate); assert_eq!(UserBookmarkEntityType::from_str("w2l_result").unwrap(), UserBookmarkEntityType::W2lResult); assert_eq!(UserBookmarkEntityType::from_str("media_file").unwrap(), UserBookmarkEntityType::MediaFile); + assert_eq!(UserBookmarkEntityType::from_str("model_weight").unwrap(), UserBookmarkEntityType::ModelWeight); assert_eq!(UserBookmarkEntityType::from_str("voice_conversion_model").unwrap(), UserBookmarkEntityType::VoiceConversionModel); assert_eq!(UserBookmarkEntityType::from_str("zs_voice").unwrap(), UserBookmarkEntityType::ZsVoice); assert!(UserBookmarkEntityType::from_str("foo").is_err()); diff --git a/crates/_types/enums/src/by_table/user_ratings/entity_type.rs b/crates/_types/enums/src/by_table/user_ratings/entity_type.rs index f463471a98..025a8e4d6c 100644 --- a/crates/_types/enums/src/by_table/user_ratings/entity_type.rs +++ b/crates/_types/enums/src/by_table/user_ratings/entity_type.rs @@ -9,6 +9,14 @@ use strum::EnumIter; #[cfg_attr(test, derive(EnumIter, EnumCount))] #[derive(Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)] pub enum UserRatingEntityType { + /// Media files (inference results, uploads, etc.) + #[serde(rename = "media_file")] + MediaFile, + + /// Model weights (modern, polymorphic, type agnostic) + #[serde(rename = "model_weight")] + ModelWeight, + /// TTS model (architecture does not matter) #[serde(rename = "tts_model")] TtsModel, @@ -34,6 +42,8 @@ impl_mysql_enum_coders!(UserRatingEntityType); impl UserRatingEntityType { pub fn to_str(&self) -> &'static str { match self { + Self::MediaFile => "media_file", + Self::ModelWeight => "model_weight", Self::TtsModel => "tts_model", Self::TtsResult => "tts_result", Self::W2lTemplate => "w2l_template", @@ -43,6 +53,8 @@ impl UserRatingEntityType { pub fn from_str(value: &str) -> Result { match value { + "media_file" => Ok(Self::MediaFile), + "model_weight" => Ok(Self::ModelWeight), "tts_model" => Ok(Self::TtsModel), "tts_result" => Ok(Self::TtsResult), "w2l_template" => Ok(Self::W2lTemplate), @@ -62,6 +74,8 @@ mod tests { #[test] fn test_serialization() { + assert_serialization(UserRatingEntityType::MediaFile, "media_file"); + assert_serialization(UserRatingEntityType::ModelWeight, "model_weight"); assert_serialization(UserRatingEntityType::TtsModel, "tts_model"); assert_serialization(UserRatingEntityType::TtsResult, "tts_result"); assert_serialization(UserRatingEntityType::W2lTemplate, "w2l_template"); @@ -74,6 +88,8 @@ mod tests { #[test] fn test_to_str() { + assert_eq!(UserRatingEntityType::MediaFile.to_str(), "media_file"); + assert_eq!(UserRatingEntityType::ModelWeight.to_str(), "model_weight"); assert_eq!(UserRatingEntityType::TtsModel.to_str(), "tts_model"); assert_eq!(UserRatingEntityType::TtsResult.to_str(), "tts_result"); assert_eq!(UserRatingEntityType::W2lTemplate.to_str(), "w2l_template"); @@ -82,6 +98,8 @@ mod tests { #[test] fn test_from_str() { + assert_eq!(UserRatingEntityType::from_str("media_file").unwrap(), UserRatingEntityType::MediaFile); + assert_eq!(UserRatingEntityType::from_str("model_weight").unwrap(), UserRatingEntityType::ModelWeight); assert_eq!(UserRatingEntityType::from_str("tts_model").unwrap(), UserRatingEntityType::TtsModel); assert_eq!(UserRatingEntityType::from_str("tts_result").unwrap(), UserRatingEntityType::TtsResult); assert_eq!(UserRatingEntityType::from_str("w2l_template").unwrap(), UserRatingEntityType::W2lTemplate); diff --git a/crates/lib/mysql_queries/src/composite_keys/by_table/user_ratings/user_rating_entity.rs b/crates/lib/mysql_queries/src/composite_keys/by_table/user_ratings/user_rating_entity.rs index a5fe4d14b5..a100f7df30 100644 --- a/crates/lib/mysql_queries/src/composite_keys/by_table/user_ratings/user_rating_entity.rs +++ b/crates/lib/mysql_queries/src/composite_keys/by_table/user_ratings/user_rating_entity.rs @@ -1,4 +1,6 @@ use enums::by_table::user_ratings::entity_type::UserRatingEntityType; +use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; use tokens::tokens::tts_models::TtsModelToken; use tokens::tokens::w2l_templates::W2lTemplateToken; @@ -12,6 +14,8 @@ use tokens::tokens::w2l_templates::W2lTemplateToken; /// NB: We probably don't want to expose this type of interface to web clients because it's /// monotonous as a JSON interface. pub enum UserRatingEntity { + MediaFile(MediaFileToken), + ModelWeight(ModelWeightToken), TtsModel(TtsModelToken), W2lTemplate(W2lTemplateToken), } @@ -20,6 +24,8 @@ pub enum UserRatingEntity { impl UserRatingEntity { pub fn get_entity_type(&self) -> UserRatingEntityType { match self { + UserRatingEntity::MediaFile(_) => UserRatingEntityType::MediaFile, + UserRatingEntity::ModelWeight(_) => UserRatingEntityType::ModelWeight, UserRatingEntity::TtsModel(_) => UserRatingEntityType::TtsModel, UserRatingEntity::W2lTemplate(_) => UserRatingEntityType::W2lTemplate, } @@ -27,6 +33,8 @@ impl UserRatingEntity { pub fn get_entity_token_str(&self) -> &str { match self { + UserRatingEntity::MediaFile(token) => token.as_str(), + UserRatingEntity::ModelWeight(token) => token.as_str(), UserRatingEntity::TtsModel(token) => token.as_str(), UserRatingEntity::W2lTemplate(token) => token.as_str(), } diff --git a/crates/lib/mysql_queries/src/queries/comments/comment_entity_token.rs b/crates/lib/mysql_queries/src/queries/comments/comment_entity_token.rs index 47f9189413..885ceac73a 100644 --- a/crates/lib/mysql_queries/src/queries/comments/comment_entity_token.rs +++ b/crates/lib/mysql_queries/src/queries/comments/comment_entity_token.rs @@ -1,10 +1,14 @@ use enums::by_table::comments::comment_entity_type::CommentEntityType; +use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; use tokens::tokens::tts_models::TtsModelToken; use tokens::tokens::w2l_templates::W2lTemplateToken; use tokens::tokens::users::UserToken; pub enum CommentEntityToken { User(UserToken), + MediaFile(MediaFileToken), + ModelWeight(ModelWeightToken), TtsModel(TtsModelToken), TtsResult(String), // TODO: Strong type W2lTemplate(W2lTemplateToken), @@ -20,6 +24,8 @@ impl CommentEntityToken { CommentEntityType::TtsResult => Self::TtsResult(token.to_string()), CommentEntityType::W2lTemplate => Self::W2lTemplate(W2lTemplateToken::new_from_str(token)), CommentEntityType::W2lResult => Self::W2lResult(token.to_string()), + CommentEntityType::MediaFile => Self::MediaFile(MediaFileToken::new_from_str(token)), + CommentEntityType::ModelWeight => Self::ModelWeight(ModelWeightToken::new_from_str(token)), } } @@ -30,6 +36,8 @@ impl CommentEntityToken { CommentEntityToken::TtsResult(tts_result_token) => (CommentEntityType::TtsResult, tts_result_token.as_str()), CommentEntityToken::W2lTemplate(w2l_template_token) => (CommentEntityType::W2lTemplate, w2l_template_token.as_str()), CommentEntityToken::W2lResult(w2l_result_token) => (CommentEntityType::W2lResult, w2l_result_token.as_str()), + CommentEntityToken::MediaFile(media_file_token) => (CommentEntityType::MediaFile, media_file_token.as_str()), + CommentEntityToken::ModelWeight(model_weight_token) => (CommentEntityType::ModelWeight, model_weight_token.as_str()), }; (entity_type, entity_token) } diff --git a/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks.rs b/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks.rs index 232c7344a6..c28ab16129 100644 --- a/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks.rs +++ b/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks.rs @@ -33,6 +33,7 @@ SELECT media_files.media_type as `maybe_media_file_type: enums::by_table::media_files::media_file_type::MediaFileType`, media_files.origin_product_category as `maybe_media_file_origin_category: enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory`, + model_weights.title as maybe_descriptive_text_model_weight_title, tts_models.title as maybe_descriptive_text_tts_model_title, tts_results.raw_inference_text as maybe_descriptive_text_tts_result_inference_text, users.display_name as maybe_descriptive_text_user_display_name, @@ -44,6 +45,7 @@ FROM JOIN users AS u ON f.user_token = u.token +LEFT OUTER JOIN model_weights ON model_weights.token = f.entity_token LEFT OUTER JOIN media_files ON media_files.token = f.entity_token LEFT OUTER JOIN tts_models ON tts_models.token = f.entity_token LEFT OUTER JOIN tts_results ON tts_results.token = f.entity_token @@ -100,6 +102,7 @@ SELECT media_files.media_type as `maybe_media_file_type: enums::by_table::media_files::media_file_type::MediaFileType`, media_files.origin_product_category as `maybe_media_file_origin_category: enums::by_table::media_files::media_file_origin_category::MediaFileOriginCategory`, + model_weights.title as maybe_descriptive_text_model_weight_title, tts_models.title as maybe_descriptive_text_tts_model_title, tts_results.raw_inference_text as maybe_descriptive_text_tts_result_inference_text, users.display_name as maybe_descriptive_text_user_display_name, @@ -111,6 +114,7 @@ FROM JOIN users AS u ON f.user_token = u.token +LEFT OUTER JOIN model_weights ON model_weights.token = f.entity_token LEFT OUTER JOIN media_files ON media_files.token = f.entity_token LEFT OUTER JOIN tts_models ON tts_models.token = f.entity_token LEFT OUTER JOIN tts_results ON tts_results.token = f.entity_token diff --git a/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks_result.rs b/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks_result.rs index e56d1de9d8..f384fbcd7e 100644 --- a/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks_result.rs +++ b/crates/lib/mysql_queries/src/queries/user_bookmarks/list_user_bookmarks_result.rs @@ -44,6 +44,7 @@ pub struct RawUserBookmarkRecord { pub (crate) maybe_media_file_type: Option, pub (crate) maybe_media_file_origin_category: Option, + pub (crate) maybe_descriptive_text_model_weight_title: Option, pub (crate) maybe_descriptive_text_tts_model_title: Option, pub (crate) maybe_descriptive_text_tts_result_inference_text: Option, pub (crate) maybe_descriptive_text_user_display_name: Option, @@ -59,6 +60,7 @@ impl RawUserBookmarkRecord { entity_token: self.entity_token, maybe_entity_descriptive_text: match self.entity_type { UserBookmarkEntityType::User => self.maybe_descriptive_text_user_display_name, + UserBookmarkEntityType::ModelWeight => self.maybe_descriptive_text_model_weight_title, UserBookmarkEntityType::TtsModel => self.maybe_descriptive_text_tts_model_title, UserBookmarkEntityType::TtsResult => self.maybe_descriptive_text_tts_result_inference_text, UserBookmarkEntityType::W2lTemplate => None, diff --git a/crates/lib/mysql_queries/src/queries/user_bookmarks/user_bookmark_entity_token.rs b/crates/lib/mysql_queries/src/queries/user_bookmarks/user_bookmark_entity_token.rs index 5fbf8224ad..aee34d7deb 100644 --- a/crates/lib/mysql_queries/src/queries/user_bookmarks/user_bookmark_entity_token.rs +++ b/crates/lib/mysql_queries/src/queries/user_bookmarks/user_bookmark_entity_token.rs @@ -1,5 +1,6 @@ use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType; use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; use tokens::tokens::tts_models::TtsModelToken; use tokens::tokens::tts_results::TtsResultToken; use tokens::tokens::users::UserToken; @@ -10,6 +11,7 @@ use tokens::tokens::zs_voices::ZsVoiceToken; pub enum UserBookmarkEntityToken { User(UserToken), + ModelWeight(ModelWeightToken), TtsModel(TtsModelToken), TtsResult(TtsResultToken), W2lTemplate(W2lTemplateToken), @@ -23,6 +25,7 @@ impl UserBookmarkEntityToken { pub fn from_entity_type_and_token(entity_type: UserBookmarkEntityType, token: &str) -> Self { match entity_type { UserBookmarkEntityType::User => Self::User(UserToken::new_from_str(token)), + UserBookmarkEntityType::ModelWeight => Self::ModelWeight(ModelWeightToken::new_from_str(token)), UserBookmarkEntityType::TtsModel => Self::TtsModel(TtsModelToken::new_from_str(token)), UserBookmarkEntityType::TtsResult => Self::TtsResult(TtsResultToken::new_from_str(token)), UserBookmarkEntityType::W2lTemplate => Self::W2lTemplate(W2lTemplateToken::new_from_str(token)), @@ -36,6 +39,7 @@ impl UserBookmarkEntityToken { pub fn get_composite_keys(&self) -> (UserBookmarkEntityType, &str) { match self { UserBookmarkEntityToken::User(user_token) => (UserBookmarkEntityType::User, user_token.as_str()), + UserBookmarkEntityToken::ModelWeight(model_weight_token) => (UserBookmarkEntityType::ModelWeight, model_weight_token.as_str()), UserBookmarkEntityToken::TtsModel(tts_model_token) => (UserBookmarkEntityType::TtsModel, tts_model_token.as_str()), UserBookmarkEntityToken::TtsResult(tts_result_token) => (UserBookmarkEntityType::TtsResult, tts_result_token.as_str()), UserBookmarkEntityToken::W2lTemplate(w2l_template_token) => (UserBookmarkEntityType::W2lTemplate, w2l_template_token.as_str()), diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/comments/create_comment_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/comments/create_comment_handler.rs index 09acffdb8b..8e6f98b2ee 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/comments/create_comment_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/comments/create_comment_handler.rs @@ -16,6 +16,13 @@ use http_server_common::request::get_request_ip::get_request_ip; use mysql_queries::queries::comments::comment_entity_token::CommentEntityToken; use mysql_queries::queries::comments::insert_comment::{insert_comment, InsertCommentArgs}; use tokens::tokens::comments::CommentToken; +use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; +use tokens::tokens::tts_models::TtsModelToken; +use tokens::tokens::tts_results::TtsResultToken; +use tokens::tokens::users::UserToken; +use tokens::tokens::w2l_results::W2lResultToken; +use tokens::tokens::w2l_templates::W2lTemplateToken; use user_input_common::check_for_slurs::contains_slurs; use user_input_common::markdown_to_html::markdown_to_html; @@ -76,6 +83,26 @@ pub async fn create_comment_handler( server_state: web::Data>, ) -> Result { + // NB(bt,2023-12-14): Kasisnu found that we're getting entity type mismatches in production. Apart from + // querying the database for entity existence, this is the next best way to prevent incorrect comment + // attachment. This is a bit of a bad process, though, since the token types are supposed to be opaque. + let token = request.entity_token.as_str(); + let token_prefix_matches = match request.entity_type { + // NB: Users had an older prefix (U:) that got replaced with the new prefix (user_) + CommentEntityType::User => token.starts_with(UserToken::token_prefix()) || token.starts_with("U:"), + CommentEntityType::MediaFile => token.starts_with(MediaFileToken::token_prefix()), + CommentEntityType::ModelWeight => token.starts_with(ModelWeightToken::token_prefix()), + CommentEntityType::TtsModel => token.starts_with(TtsModelToken::token_prefix()), + CommentEntityType::TtsResult => token.starts_with(TtsResultToken::token_prefix()), + CommentEntityType::W2lTemplate => token.starts_with(W2lTemplateToken::token_prefix()), + CommentEntityType::W2lResult => token.starts_with(W2lResultToken::token_prefix()), + }; + + if !token_prefix_matches { + warn!("invalid token prefix: {:?} for {:?}", request.entity_token, request.entity_type); + return Err(CreateCommentError::BadInput("invalid token prefix".to_string())); + } + let mut mysql_connection = server_state.mysql_pool .acquire() .await diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs index b62fe2feeb..43f0fe6ac1 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_bookmarks/create_user_bookmark_handler.rs @@ -12,9 +12,19 @@ use actix_web::http::StatusCode; use log::warn; use enums::by_table::user_bookmarks::user_bookmark_entity_type::UserBookmarkEntityType; -use mysql_queries::queries::user_bookmarks::user_bookmark_entity_token::UserBookmarkEntityToken; use mysql_queries::queries::user_bookmarks::create_user_bookmark::{create_user_bookmark, CreateUserBookmarkArgs}; +use mysql_queries::queries::user_bookmarks::user_bookmark_entity_token::UserBookmarkEntityToken; +use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; +use tokens::tokens::tts_models::TtsModelToken; +use tokens::tokens::tts_results::TtsResultToken; use tokens::tokens::user_bookmarks::UserBookmarkToken; +use tokens::tokens::users::UserToken; +use tokens::tokens::voice_conversion_models::VoiceConversionModelToken; +use tokens::tokens::w2l_results::W2lResultToken; +use tokens::tokens::w2l_templates::W2lTemplateToken; +use tokens::tokens::zs_voices::ZsVoiceToken; + use crate::http_server::web_utils::response_error_helpers::to_simple_json_error; use crate::server_state::ServerState; @@ -70,6 +80,28 @@ pub async fn create_user_bookmark_handler( server_state: web::Data>, ) -> Result { + // NB(bt,2023-12-14): Kasisnu found that we're getting entity type mismatches in production. Apart from + // querying the database for entity existence, this is the next best way to prevent incorrect comment + // attachment. This is a bit of a bad process, though, since the token types are supposed to be opaque. + let token = request.entity_token.as_str(); + let token_prefix_matches = match request.entity_type { + // NB: Users had an older prefix (U:) that got replaced with the new prefix (user_) + UserBookmarkEntityType::User => token.starts_with(UserToken::token_prefix()) || token.starts_with("U:"), + UserBookmarkEntityType::MediaFile => token.starts_with(MediaFileToken::token_prefix()), + UserBookmarkEntityType::ModelWeight => token.starts_with(ModelWeightToken::token_prefix()), + UserBookmarkEntityType::TtsModel => token.starts_with(TtsModelToken::token_prefix()), + UserBookmarkEntityType::TtsResult => token.starts_with(TtsResultToken::token_prefix()), + UserBookmarkEntityType::W2lTemplate => token.starts_with(W2lTemplateToken::token_prefix()), + UserBookmarkEntityType::W2lResult => token.starts_with(W2lResultToken::token_prefix()), + UserBookmarkEntityType::VoiceConversionModel => token.starts_with(VoiceConversionModelToken::token_prefix()), + UserBookmarkEntityType::ZsVoice => token.starts_with(ZsVoiceToken::token_prefix()), + }; + + if !token_prefix_matches { + warn!("invalid token prefix: {:?} for {:?}", request.entity_token, request.entity_type); + return Err(CreateUserBookmarkError::BadInput("invalid token prefix".to_string())); + } + let mut mysql_connection = server_state.mysql_pool .acquire() .await diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/get_user_rating_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/get_user_rating_handler.rs index 228b053afa..5c7f077910 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/get_user_rating_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/get_user_rating_handler.rs @@ -9,6 +9,8 @@ use enums::by_table::user_ratings::rating_value::UserRatingValue; use http_server_common::response::serialize_as_json_error::serialize_as_json_error; use mysql_queries::composite_keys::by_table::user_ratings::user_rating_entity::UserRatingEntity; use mysql_queries::queries::users::user_ratings::get_user_rating::{Args, get_user_rating}; +use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; use tokens::tokens::tts_models::TtsModelToken; use tokens::tokens::w2l_templates::W2lTemplateToken; @@ -105,9 +107,12 @@ pub async fn get_user_rating_handler( UserRatingEntityType::W2lTemplate => UserRatingEntity::W2lTemplate( W2lTemplateToken::new_from_str(&path.entity_token)), + UserRatingEntityType::MediaFile => UserRatingEntity::MediaFile(MediaFileToken::new_from_str(&path.entity_token)), + UserRatingEntityType::ModelWeight => UserRatingEntity::ModelWeight(ModelWeightToken::new_from_str(&path.entity_token)), + // TODO: We'll handle ratings of more types in the future. UserRatingEntityType::W2lResult | UserRatingEntityType::TtsResult => - return Err(GetUserRatingError::BadInput("type not yet supported".to_string())) + return Err(GetUserRatingError::BadInput("type not yet supported".to_string())), }; let maybe_rating = get_user_rating(Args { diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/set_user_rating_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/set_user_rating_handler.rs index 8d5af40699..e25a3d6e85 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/set_user_rating_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/user_ratings/set_user_rating_handler.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use actix_web::{HttpRequest, HttpResponse, ResponseError, web}; use actix_web::http::StatusCode; -use log::{error, info}; +use log::{error, info, warn}; use enums::by_table::user_ratings::entity_type::UserRatingEntityType; use enums::by_table::user_ratings::rating_value::UserRatingValue; @@ -11,7 +11,11 @@ use http_server_common::response::serialize_as_json_error::serialize_as_json_err use mysql_queries::composite_keys::by_table::user_ratings::user_rating_entity::UserRatingEntity; use mysql_queries::queries::users::user_ratings::update_tts_model_ratings::update_tts_model_ratings; use mysql_queries::queries::users::user_ratings::upsert_user_rating::{Args, upsert_user_rating}; +use tokens::tokens::media_files::MediaFileToken; +use tokens::tokens::model_weights::ModelWeightToken; use tokens::tokens::tts_models::TtsModelToken; +use tokens::tokens::tts_results::TtsResultToken; +use tokens::tokens::w2l_results::W2lResultToken; use tokens::tokens::w2l_templates::W2lTemplateToken; use crate::server_state::ServerState; @@ -73,6 +77,27 @@ pub async fn set_user_rating_handler( request: web::Json, server_state: web::Data>) -> Result { + // NB(bt,2023-12-14): Kasisnu found that we're getting entity type mismatches in production. Apart from + // querying the database for entity existence, this is the next best way to prevent incorrect comment + // attachment. This is a bit of a bad process, though, since the token types are supposed to be opaque. + let token = request.entity_token.as_str(); + let token_prefix_matches = match request.entity_type { + // NB: Users had an older prefix (U:) that got replaced with the new prefix (user_) + UserRatingEntityType::MediaFile => token.starts_with(MediaFileToken::token_prefix()), + UserRatingEntityType::ModelWeight => token.starts_with(ModelWeightToken::token_prefix()), + UserRatingEntityType::TtsModel => token.starts_with(TtsModelToken::token_prefix()), + UserRatingEntityType::TtsResult => token.starts_with(TtsResultToken::token_prefix()), + UserRatingEntityType::W2lTemplate => token.starts_with(W2lTemplateToken::token_prefix()), + UserRatingEntityType::W2lResult => token.starts_with(W2lResultToken::token_prefix()), + //UserRatingEntityType::VoiceConversionModel => token.starts_with(VoiceConversionModelToken::token_prefix()), + //UserRatingEntityType::ZsVoice => token.starts_with(ZsVoiceToken::token_prefix()), + }; + + if !token_prefix_matches { + warn!("invalid token prefix: {:?} for {:?}", request.entity_token, request.entity_type); + return Err(SetUserRatingError::BadInput("invalid token prefix".to_string())); + } + let mut mysql_connection = server_state.mysql_pool.acquire() .await .map_err(|e| { @@ -100,6 +125,9 @@ pub async fn set_user_rating_handler( let ip_address = get_request_ip(&http_request); let entity= match request.entity_type { + UserRatingEntityType::MediaFile => UserRatingEntity::MediaFile(MediaFileToken::new_from_str(&request.entity_token)), + UserRatingEntityType::ModelWeight => UserRatingEntity::ModelWeight(ModelWeightToken::new_from_str(&request.entity_token)), + UserRatingEntityType::TtsModel => UserRatingEntity::TtsModel( TtsModelToken::new_from_str(&request.entity_token)), @@ -108,7 +136,7 @@ pub async fn set_user_rating_handler( // TODO: We'll handle ratings of more types in the future. UserRatingEntityType::W2lResult | UserRatingEntityType::TtsResult => - return Err(SetUserRatingError::BadInput("type not yet supported".to_string())) + return Err(SetUserRatingError::BadInput("type not yet supported".to_string())), }; let _r = upsert_user_rating(Args {