diff --git a/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_email.rs b/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_email.rs index c68bc3eb3a..de63bc0110 100644 --- a/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_email.rs +++ b/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_email.rs @@ -16,7 +16,8 @@ SELECT email_address, password_hash as `password_hash: crate::queries::users::user::lookup_user_for_login_result::VecBytes`, password_version, - is_banned + is_banned, + maybe_feature_flags FROM users WHERE email_address = ? LIMIT 1 diff --git a/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_username.rs b/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_username.rs index 6f6927fee9..73a68593d5 100644 --- a/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_username.rs +++ b/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_by_username.rs @@ -16,7 +16,8 @@ SELECT email_address, password_hash as `password_hash: crate::queries::users::user::lookup_user_for_login_result::VecBytes`, password_version, - is_banned + is_banned, + maybe_feature_flags FROM users WHERE username = ? LIMIT 1 diff --git a/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_result.rs b/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_result.rs index aaf04f0abe..b0f1242c6d 100644 --- a/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_result.rs +++ b/crates/lib/mysql_queries/src/queries/users/user/lookup_user_for_login_result.rs @@ -13,4 +13,7 @@ pub struct UserRecordForLogin { pub password_hash: VecBytes, pub password_version: u32, pub is_banned: i8, + + /// Optional comma-separated list of parseable `UserFeatureFlag` enum features + pub maybe_feature_flags: Option, } diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/users/create_account_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/users/create_account_handler.rs index 785f8e32ae..72297acd15 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/users/create_account_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/users/create_account_handler.rs @@ -34,6 +34,7 @@ use crate::http_server::validations::is_reserved_username::is_reserved_username; use crate::http_server::validations::validate_passwords::validate_passwords; use crate::http_server::validations::validate_username::validate_username; use crate::util::email_to_gravatar::email_to_gravatar; +use crate::util::enroll_in_studio::enroll_in_studio; #[derive(Deserialize)] pub struct CreateAccountRequest { @@ -263,10 +264,10 @@ pub async fn create_account_handler( })?; // NB: Enroll new users in studio for a while. - //enroll_in_studio(&new_user_data.user_token, &ip_address, &mysql_pool).await - // .map_err(|e| { - // warn!("error enrolling in studio: {:?}", e); - // }).ok(); + enroll_in_studio(&new_user_data.user_token, &ip_address, &mysql_pool, None).await + .map_err(|e| { + warn!("error enrolling in studio: {:?}", e); + }).ok(); let session_token = UserSessionToken::new_from_str(&session_token); @@ -293,37 +294,3 @@ pub async fn create_account_handler( .content_type("application/json") .body(body)) } - -async fn enroll_in_studio( - user_token: &UserToken, - ip_address: &str, - mysql_pool: &MySqlPool, -) -> AnyhowResult<()> { - let mut user_feature_flags = UserSessionFeatureFlags::empty(); - - user_feature_flags.add_flags([ - UserFeatureFlag::Studio, - UserFeatureFlag::VideoStyleTransfer, - ]); - - let mut transaction = mysql_pool.begin().await?; - - set_user_feature_flags_transactional(SetUserFeatureFlagTransactionalArgs { - subject_user_token: user_token, - maybe_feature_flags: user_feature_flags.maybe_serialize_string().as_deref(), - maybe_mod_user_token: None, - ip_address: &ip_address, - transaction: &mut transaction, - }).await?; - - // NB: This isn't a necessary field, but can be useful for analytics. - set_can_access_studio_transactional(SetCanAccessStudioArgs { - subject_user_token: user_token, - can_access_studio: true, - transaction: &mut transaction, - }).await?; - - transaction.commit().await?; - - Ok(()) -} diff --git a/crates/service/web/storyteller_web/src/http_server/endpoints/users/login_handler.rs b/crates/service/web/storyteller_web/src/http_server/endpoints/users/login_handler.rs index 499c2256e7..6624b65368 100644 --- a/crates/service/web/storyteller_web/src/http_server/endpoints/users/login_handler.rs +++ b/crates/service/web/storyteller_web/src/http_server/endpoints/users/login_handler.rs @@ -23,6 +23,8 @@ use password::bcrypt_confirm_password::bcrypt_confirm_password; use tokens::tokens::user_sessions::UserSessionToken; use crate::http_server::session::http::http_user_session_manager::HttpUserSessionManager; +use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags; +use crate::util::enroll_in_studio::enroll_in_studio; #[derive(Deserialize, ToSchema)] pub struct LoginRequest { @@ -175,6 +177,17 @@ pub async fn login_handler( info!("login session created for user: {} / {:?}", &user.token, &user.token); + + let user_feature_flags = + UserSessionFeatureFlags::new(user.maybe_feature_flags.as_deref()); + + // NB: Enroll new users in studio for a while. + enroll_in_studio(&user.token, &ip_address, &mysql_pool, Some(&user_feature_flags)).await + .map_err(|e| { + warn!("error enrolling in studio: {:?}", e); + }).ok(); + + let session_token = UserSessionToken::new_from_str(&session_token); let session_cookie = match session_cookie_manager.create_cookie(&session_token, &user.token) { diff --git a/crates/service/web/storyteller_web/src/util/enroll_in_studio.rs b/crates/service/web/storyteller_web/src/util/enroll_in_studio.rs new file mode 100644 index 0000000000..ae2c1ca454 --- /dev/null +++ b/crates/service/web/storyteller_web/src/util/enroll_in_studio.rs @@ -0,0 +1,47 @@ +use sqlx::MySqlPool; + +use enums::by_table::users::user_feature_flag::UserFeatureFlag; +use errors::AnyhowResult; +use mysql_queries::queries::users::user::set_can_access_studio_transactional::{set_can_access_studio_transactional, SetCanAccessStudioArgs}; +use mysql_queries::queries::users::user::set_user_feature_flags_transactional::{set_user_feature_flags_transactional, SetUserFeatureFlagTransactionalArgs}; +use tokens::tokens::users::UserToken; + +use crate::http_server::session::lookup::user_session_feature_flags::UserSessionFeatureFlags; + +pub async fn enroll_in_studio( + user_token: &UserToken, + ip_address: &str, + mysql_pool: &MySqlPool, + maybe_existing_feature_flags: Option<&UserSessionFeatureFlags> +) -> AnyhowResult<()> { + + let mut user_feature_flags = maybe_existing_feature_flags + .map(|ff| ff.clone()) + .unwrap_or_else(|| UserSessionFeatureFlags::empty()); + + user_feature_flags.add_flags([ + UserFeatureFlag::Studio, + UserFeatureFlag::VideoStyleTransfer, + ]); + + let mut transaction = mysql_pool.begin().await?; + + set_user_feature_flags_transactional(SetUserFeatureFlagTransactionalArgs { + subject_user_token: user_token, + maybe_feature_flags: user_feature_flags.maybe_serialize_string().as_deref(), + maybe_mod_user_token: None, + ip_address: &ip_address, + transaction: &mut transaction, + }).await?; + + // NB: This isn't a necessary field, but can be useful for analytics. + set_can_access_studio_transactional(SetCanAccessStudioArgs { + subject_user_token: user_token, + can_access_studio: true, + transaction: &mut transaction, + }).await?; + + transaction.commit().await?; + + Ok(()) +} diff --git a/crates/service/web/storyteller_web/src/util/mod.rs b/crates/service/web/storyteller_web/src/util/mod.rs index fba5a5c01a..a69e1da649 100644 --- a/crates/service/web/storyteller_web/src/util/mod.rs +++ b/crates/service/web/storyteller_web/src/util/mod.rs @@ -5,5 +5,6 @@ pub mod check_creator_tokens; pub mod delete_role_disambiguation; pub mod email_to_gravatar; pub mod encrypted_sort_id; +pub mod enroll_in_studio; pub mod placeholder_images; pub mod troll_user_bans; \ No newline at end of file