From a329af914173701e87ee0427ff546760a2221a16 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Fri, 28 Feb 2025 13:01:23 +0200 Subject: [PATCH 1/3] feat(Auth): impl apple token auth --- src/types/api/request.rs | 12 ++ src/types/profile/user.rs | 3 + src/unit_tests/ctx/add_to_library.rs | 1 + src/unit_tests/ctx/authenticate.rs | 142 ++++++++++++++++++++ src/unit_tests/ctx/delete_account.rs | 1 + src/unit_tests/ctx/install_addon.rs | 1 + src/unit_tests/ctx/logout.rs | 1 + src/unit_tests/ctx/pull_addons_from_api.rs | 1 + src/unit_tests/ctx/push_addons_to_api.rs | 1 + src/unit_tests/ctx/remove_from_library.rs | 1 + src/unit_tests/ctx/rewind_library_item.rs | 1 + src/unit_tests/ctx/sync_library_with_api.rs | 2 + src/unit_tests/ctx/uninstall_addon.rs | 1 + src/unit_tests/serde/auth_request.rs | 17 ++- src/unit_tests/serde/user.rs | 4 + stremio-core-web/src/env.rs | 1 + 16 files changed, 189 insertions(+), 1 deletion(-) diff --git a/src/types/api/request.rs b/src/types/api/request.rs index 294d9d6a2..724348f40 100644 --- a/src/types/api/request.rs +++ b/src/types/api/request.rs @@ -158,6 +158,7 @@ impl FetchRequestParams for APIRequest { APIRequest::Auth(AuthRequest::Login { .. }) => "login".to_owned(), APIRequest::Auth(AuthRequest::LoginWithToken { .. }) => "loginWithToken".to_owned(), APIRequest::Auth(AuthRequest::Facebook { .. }) => "authWithFacebook".to_owned(), + APIRequest::Auth(AuthRequest::Apple { .. }) => "authWithApple".to_owned(), APIRequest::Auth(AuthRequest::Register { .. }) => "register".to_owned(), APIRequest::Logout { .. } => "logout".to_owned(), APIRequest::DeleteAccount { .. } => "deleteUser".to_owned(), @@ -192,6 +193,8 @@ pub enum AuthRequest { password: String, #[serde(default)] facebook: bool, + #[serde(default)] + apple: bool, }, Register { email: String, @@ -201,6 +204,9 @@ pub enum AuthRequest { Facebook { token: String, }, + Apple { + token: String, + }, LoginWithToken { token: String, }, @@ -213,11 +219,13 @@ impl fmt::Debug for AuthRequest { email, password: _, facebook, + apple, } => f .debug_struct("Login") .field("email", email) .field("password", &"") .field("facebook", facebook) + .field("apple", apple) .finish(), Self::Register { email, @@ -233,6 +241,10 @@ impl fmt::Debug for AuthRequest { .debug_struct("Facebook") .field("token", &"") .finish(), + Self::Apple { token: _ } => f + .debug_struct("Apple") + .field("token", &"") + .finish(), Self::LoginWithToken { token: _ } => f .debug_struct("LoginWithToken") .field("token", &"") diff --git a/src/types/profile/user.rs b/src/types/profile/user.rs index 7be1e402a..ea425515c 100644 --- a/src/types/profile/user.rs +++ b/src/types/profile/user.rs @@ -58,6 +58,9 @@ pub struct User { pub fb_id: Option, #[serde(default)] #[serde_as(deserialize_as = "DefaultOnNull")] + pub apple_id: Option, + #[serde(default)] + #[serde_as(deserialize_as = "DefaultOnNull")] pub avatar: Option, #[cfg_attr(test, derivative(Default(value = "Utc.timestamp_opt(0, 0).unwrap()")))] pub last_modified: DateTime, diff --git a/src/unit_tests/ctx/add_to_library.rs b/src/unit_tests/ctx/add_to_library.rs index bf8630335..71a136933 100644 --- a/src/unit_tests/ctx/add_to_library.rs +++ b/src/unit_tests/ctx/add_to_library.rs @@ -87,6 +87,7 @@ fn actionctx_addtolibrary() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/authenticate.rs b/src/unit_tests/ctx/authenticate.rs index 7541d835d..11eec5fe8 100644 --- a/src/unit_tests/ctx/authenticate.rs +++ b/src/unit_tests/ctx/authenticate.rs @@ -27,6 +27,7 @@ fn user_fixture() -> User { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), @@ -121,6 +122,7 @@ fn actionctx_authenticate_login() { email: "user_email".into(), password: "user_password".into(), facebook: false, + apple: false, })), }) }); @@ -484,6 +486,146 @@ fn actionctx_authenticate_facebook() { ); } +#[test] +fn actionctx_authenticate_apple() { + #[derive(Model, Clone, Default)] + #[model(TestEnv)] + struct TestModel { + ctx: Ctx, + } + fn fetch_handler(request: Request) -> TryEnvFuture> { + match request { + Request { + url, method, body, .. + } if url == "https://api.strem.io/api/authWithApple" + && method == "POST" + && body == "{\"type\":\"Auth\",\"type\":\"Apple\",\"token\":\"access_token\"}" => + { + future::ok(Box::new(APIResult::Ok(auth_response_fixture())) as Box).boxed_env() + } + Request { + url, method, body, .. + } if url == "https://api.strem.io/api/addonCollectionGet" + && method == "POST" + && body == "{\"type\":\"AddonCollectionGet\",\"authKey\":\"auth_key\",\"update\":true}" => + { + future::ok(Box::new(APIResult::Ok( + CollectionResponse { + addons: vec![], + last_modified: TestEnv::now(), + },) + ) as Box).boxed_env() + } + Request { + url, method, body, .. + } if url == "https://api.strem.io/api/datastoreGet" + && method == "POST" + && body == "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"ids\":[],\"all\":true}" => + { + future::ok(Box::new(APIResult::Ok(LibraryItemsResponse::new(),)) as Box).boxed_env() + } + _ => default_fetch_handler(request), + } + } + let _env_mutex = TestEnv::reset().expect("Should have exclusive lock to TestEnv"); + *FETCH_HANDLER.write().unwrap() = Box::new(fetch_handler); + let ctx = Ctx::new( + Profile::default(), + LibraryBucket::default(), + StreamsBucket::default(), + ServerUrlsBucket::new::(None), + NotificationsBucket::new::(None, vec![]), + SearchHistoryBucket::default(), + DismissedEventsBucket::default(), + ); + let (runtime, _rx) = Runtime::::new(TestModel { ctx }, vec![], 1000); + TestEnv::run(|| { + runtime.dispatch(RuntimeAction { + field: None, + action: Action::Ctx(ActionCtx::Authenticate(AuthRequest::Apple { + token: "access_token".into(), + })), + }) + }); + assert_eq!( + runtime.model().unwrap().ctx.profile, + profile_fixture(), + "profile updated successfully in memory" + ); + assert_eq!( + runtime.model().unwrap().ctx.library, + LibraryBucket { + uid: Some("user_id".to_string()), + ..Default::default() + }, + "library updated successfully in memory" + ); + assert_eq!( + serde_json::from_str::(STORAGE.read().unwrap().get(PROFILE_STORAGE_KEY).unwrap()) + .unwrap(), + profile_fixture(), + "profile updated successfully in storage" + ); + assert_eq!( + serde_json::from_str::( + STORAGE + .read() + .unwrap() + .get(LIBRARY_RECENT_STORAGE_KEY) + .unwrap() + ) + .unwrap(), + LibraryBucket::new(Some("user_id".to_owned()), vec![]), + "recent library updated successfully in storage" + ); + assert_eq!( + serde_json::from_str::( + STORAGE.read().unwrap().get(LIBRARY_STORAGE_KEY).unwrap() + ) + .unwrap(), + LibraryBucket::new(Some("user_id".to_owned()), vec![]), + "library updated successfully in storage" + ); + assert_eq!( + REQUESTS.read().unwrap().len(), + 3, + "Three requests have been sent" + ); + assert_eq!( + REQUESTS.read().unwrap().first().unwrap().to_owned(), + Request { + url: "https://api.strem.io/api/authWithApple".to_owned(), + method: "POST".to_owned(), + body: "{\"type\":\"Auth\",\"type\":\"Apple\",\"token\":\"access_token\"}".to_owned(), + ..Default::default() + }, + "Login request has been sent" + ); + assert_eq!( + REQUESTS.read().unwrap().get(1).unwrap().to_owned(), + Request { + url: "https://api.strem.io/api/addonCollectionGet".to_owned(), + method: "POST".to_owned(), + body: "{\"type\":\"AddonCollectionGet\",\"authKey\":\"auth_key\",\"update\":true}" + .to_owned(), + ..Default::default() + }, + "AddonCollectionGet request has been sent" + ); + assert_eq!( + REQUESTS.read().unwrap().get(2).unwrap().to_owned(), + Request { + url: "https://api.strem.io/api/datastoreGet".to_owned(), + method: "POST".to_owned(), + body: + "{\"authKey\":\"auth_key\",\"collection\":\"libraryItem\",\"ids\":[],\"all\":true}" + .to_owned(), + ..Default::default() + }, + "DatastoreGet request has been sent" + ); +} + #[test] fn actionctx_authenticate_register() { #[derive(Model, Clone, Default)] diff --git a/src/unit_tests/ctx/delete_account.rs b/src/unit_tests/ctx/delete_account.rs index 739791ada..4e944e44d 100644 --- a/src/unit_tests/ctx/delete_account.rs +++ b/src/unit_tests/ctx/delete_account.rs @@ -55,6 +55,7 @@ fn actionctx_delete_account() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/install_addon.rs b/src/unit_tests/ctx/install_addon.rs index ff9e70fb4..db8add3cc 100644 --- a/src/unit_tests/ctx/install_addon.rs +++ b/src/unit_tests/ctx/install_addon.rs @@ -145,6 +145,7 @@ fn actionctx_installaddon_install_with_user() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/logout.rs b/src/unit_tests/ctx/logout.rs index f00ea119b..a6a8bece8 100644 --- a/src/unit_tests/ctx/logout.rs +++ b/src/unit_tests/ctx/logout.rs @@ -49,6 +49,7 @@ fn actionctx_logout() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/pull_addons_from_api.rs b/src/unit_tests/ctx/pull_addons_from_api.rs index c8d123c0c..95e9d31e4 100644 --- a/src/unit_tests/ctx/pull_addons_from_api.rs +++ b/src/unit_tests/ctx/pull_addons_from_api.rs @@ -118,6 +118,7 @@ fn actionctx_pulladdonsfromapi_with_user() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/push_addons_to_api.rs b/src/unit_tests/ctx/push_addons_to_api.rs index c130a7f30..26838301c 100644 --- a/src/unit_tests/ctx/push_addons_to_api.rs +++ b/src/unit_tests/ctx/push_addons_to_api.rs @@ -108,6 +108,7 @@ fn actionctx_pushaddonstoapi_with_user() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/remove_from_library.rs b/src/unit_tests/ctx/remove_from_library.rs index 83b1bfd01..762027c51 100644 --- a/src/unit_tests/ctx/remove_from_library.rs +++ b/src/unit_tests/ctx/remove_from_library.rs @@ -80,6 +80,7 @@ fn actionctx_removefromlibrary() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/rewind_library_item.rs b/src/unit_tests/ctx/rewind_library_item.rs index ff358dc15..8a873b73b 100644 --- a/src/unit_tests/ctx/rewind_library_item.rs +++ b/src/unit_tests/ctx/rewind_library_item.rs @@ -88,6 +88,7 @@ fn actionctx_rewindlibraryitem() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/sync_library_with_api.rs b/src/unit_tests/ctx/sync_library_with_api.rs index 0ddda97a0..b280a4d63 100644 --- a/src/unit_tests/ctx/sync_library_with_api.rs +++ b/src/unit_tests/ctx/sync_library_with_api.rs @@ -249,6 +249,7 @@ fn actionctx_synclibrarywithapi_with_user() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), @@ -414,6 +415,7 @@ fn actionctx_synclibrarywithapi_with_user_empty_library() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/ctx/uninstall_addon.rs b/src/unit_tests/ctx/uninstall_addon.rs index 009e32b94..96d49f549 100644 --- a/src/unit_tests/ctx/uninstall_addon.rs +++ b/src/unit_tests/ctx/uninstall_addon.rs @@ -195,6 +195,7 @@ fn actionctx_uninstalladdon_with_user() { id: "user_id".to_owned(), email: "user_email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: TestEnv::now(), date_registered: TestEnv::now(), diff --git a/src/unit_tests/serde/auth_request.rs b/src/unit_tests/serde/auth_request.rs index 2848536e5..e925de469 100644 --- a/src/unit_tests/serde/auth_request.rs +++ b/src/unit_tests/serde/auth_request.rs @@ -11,6 +11,7 @@ fn auth_request() { email: "email".to_owned(), password: "password".to_owned(), facebook: false, + apple: false, }, AuthRequest::LoginWithToken { token: "token".to_owned(), @@ -18,6 +19,9 @@ fn auth_request() { AuthRequest::Facebook { token: "token".to_owned(), }, + AuthRequest::Apple { + token: "token".to_owned(), + }, AuthRequest::Register { email: "email".to_owned(), password: "password".to_owned(), @@ -26,7 +30,7 @@ fn auth_request() { ], &[ vec![ - Token::Seq { len: Some(4) }, + Token::Seq { len: Some(5) }, Token::Struct { name: "AuthRequest", len: 4, @@ -39,6 +43,8 @@ fn auth_request() { Token::Str("password"), Token::Str("facebook"), Token::Bool(false), + Token::Str("apple"), + Token::Bool(false), Token::StructEnd, Token::Struct { name: "AuthRequest", @@ -58,6 +64,15 @@ fn auth_request() { Token::Str("token"), Token::Str("token"), Token::StructEnd, + Token::Struct { + name: "AuthRequest", + len: 2, + }, + Token::Str("type"), + Token::Str("Apple"), + Token::Str("token"), + Token::Str("token"), + Token::StructEnd, Token::Struct { name: "AuthRequest", len: 4, diff --git a/src/unit_tests/serde/user.rs b/src/unit_tests/serde/user.rs index 7f18adca1..0ce0a6fa9 100644 --- a/src/unit_tests/serde/user.rs +++ b/src/unit_tests/serde/user.rs @@ -11,6 +11,7 @@ fn user() { id: "id".to_owned(), email: "email".to_owned(), fb_id: Some("fb_id".to_owned()), + apple_id: Some("apple_id".to_owned()), avatar: Some("avatar".to_owned()), last_modified: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), date_registered: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), @@ -22,6 +23,7 @@ fn user() { id: "id".to_owned(), email: "email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), date_registered: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), @@ -45,6 +47,7 @@ fn user() { Token::Str("fbId"), Token::Some, Token::Str("fb_id"), + Token::Str("appleId"), Token::Str("avatar"), Token::Some, Token::Str("avatar"), @@ -93,6 +96,7 @@ fn user() { id: "id".to_owned(), email: "email".to_owned(), fb_id: None, + apple_id: None, avatar: None, last_modified: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), date_registered: Utc.with_ymd_and_hms(2020, 1, 1, 0, 0, 0).unwrap(), diff --git a/stremio-core-web/src/env.rs b/stremio-core-web/src/env.rs index 91f1f2478..ed5573c73 100644 --- a/stremio-core-web/src/env.rs +++ b/stremio-core-web/src/env.rs @@ -152,6 +152,7 @@ impl WebEnv { AuthRequest::Login { facebook, .. } if *facebook => "facebook", AuthRequest::Login { .. } => "login", AuthRequest::Facebook { .. } => "authWithFacebook", + AuthRequest::Apple { .. } => "authWithApple", AuthRequest::LoginWithToken { .. } => "loginWithToken", AuthRequest::Register { .. } => "register", }, From e8ebbc78138943fee2d378dad1eab7650ab48ed0 Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Fri, 28 Feb 2025 14:00:52 +0200 Subject: [PATCH 2/3] fix(tests): user --- src/unit_tests/serde/user.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/unit_tests/serde/user.rs b/src/unit_tests/serde/user.rs index 0ce0a6fa9..b851a8595 100644 --- a/src/unit_tests/serde/user.rs +++ b/src/unit_tests/serde/user.rs @@ -38,7 +38,7 @@ fn user() { Token::Seq { len: Some(2) }, Token::Struct { name: "User", - len: 9, + len: 10, }, Token::Str("_id"), Token::Str("id"), @@ -47,7 +47,8 @@ fn user() { Token::Str("fbId"), Token::Some, Token::Str("fb_id"), - Token::Str("appleId"), + Token::Str("apple_id"), + Token::Some, Token::Str("avatar"), Token::Some, Token::Str("avatar"), @@ -66,7 +67,7 @@ fn user() { Token::StructEnd, Token::Struct { name: "User", - len: 9, + len: 10, }, Token::Str("_id"), Token::Str("id"), @@ -74,6 +75,8 @@ fn user() { Token::Str("email"), Token::Str("fbId"), Token::None, + Token::Str("appleId"), + Token::None, Token::Str("avatar"), Token::None, Token::Str("lastModified"), From 0ac080205ec88b1c16bf12276c5fd6cc8ca2e35f Mon Sep 17 00:00:00 2001 From: "Timothy Z." Date: Fri, 28 Feb 2025 14:16:49 +0200 Subject: [PATCH 3/3] fix(unit_tests): new field tests --- src/unit_tests/ctx/authenticate.rs | 4 ++-- src/unit_tests/serde/auth_request.rs | 2 +- src/unit_tests/serde/default_tokens_ext.rs | 8 ++++++-- src/unit_tests/serde/user.rs | 3 ++- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/unit_tests/ctx/authenticate.rs b/src/unit_tests/ctx/authenticate.rs index 11eec5fe8..a4d1cdbcf 100644 --- a/src/unit_tests/ctx/authenticate.rs +++ b/src/unit_tests/ctx/authenticate.rs @@ -73,7 +73,7 @@ fn actionctx_authenticate_login() { url, method, body, .. } if url == "https://api.strem.io/api/login" && method == "POST" - && body == "{\"type\":\"Auth\",\"type\":\"Login\",\"email\":\"user_email\",\"password\":\"user_password\",\"facebook\":false}" => + && body == "{\"type\":\"Auth\",\"type\":\"Login\",\"email\":\"user_email\",\"password\":\"user_password\",\"facebook\":false,\"apple\":false}" => { future::ok(Box::new(APIResult::Ok(auth_response_fixture())) as Box).boxed_env() } @@ -175,7 +175,7 @@ fn actionctx_authenticate_login() { Request { url: "https://api.strem.io/api/login".to_owned(), method: "POST".to_owned(), - body: "{\"type\":\"Auth\",\"type\":\"Login\",\"email\":\"user_email\",\"password\":\"user_password\",\"facebook\":false}".to_owned(), + body: "{\"type\":\"Auth\",\"type\":\"Login\",\"email\":\"user_email\",\"password\":\"user_password\",\"facebook\":false,\"apple\":false}".to_owned(), ..Default::default() }, "Login request has been sent" diff --git a/src/unit_tests/serde/auth_request.rs b/src/unit_tests/serde/auth_request.rs index e925de469..8f8c87328 100644 --- a/src/unit_tests/serde/auth_request.rs +++ b/src/unit_tests/serde/auth_request.rs @@ -33,7 +33,7 @@ fn auth_request() { Token::Seq { len: Some(5) }, Token::Struct { name: "AuthRequest", - len: 4, + len: 5, }, Token::Str("type"), Token::Str("Login"), diff --git a/src/unit_tests/serde/default_tokens_ext.rs b/src/unit_tests/serde/default_tokens_ext.rs index b8c2dcea5..b97fd57d9 100644 --- a/src/unit_tests/serde/default_tokens_ext.rs +++ b/src/unit_tests/serde/default_tokens_ext.rs @@ -323,7 +323,7 @@ impl DefaultTokens for User { vec![ Token::Struct { name: "User", - len: 9, + len: 10, }, Token::Str("_id"), Token::Str(""), @@ -331,6 +331,8 @@ impl DefaultTokens for User { Token::Str(""), Token::Str("fbId"), Token::None, + Token::Str("appleId"), + Token::None, Token::Str("avatar"), Token::None, Token::Str("lastModified"), @@ -451,7 +453,7 @@ impl DefaultTokens for AuthRequest { vec![ Token::Struct { name: "AuthRequest", - len: 5, + len: 6, }, Token::Str("type"), Token::Str("Auth"), @@ -463,6 +465,8 @@ impl DefaultTokens for AuthRequest { Token::Str(""), Token::Str("facebook"), Token::Bool(false), + Token::Str("apple"), + Token::Bool(false), Token::StructEnd, ] } diff --git a/src/unit_tests/serde/user.rs b/src/unit_tests/serde/user.rs index b851a8595..9b4999e13 100644 --- a/src/unit_tests/serde/user.rs +++ b/src/unit_tests/serde/user.rs @@ -47,8 +47,9 @@ fn user() { Token::Str("fbId"), Token::Some, Token::Str("fb_id"), - Token::Str("apple_id"), + Token::Str("appleId"), Token::Some, + Token::Str("apple_id"), Token::Str("avatar"), Token::Some, Token::Str("avatar"),