Skip to content

Commit

Permalink
Merge pull request #753 from Stremio/feat/ctx-delete-account
Browse files Browse the repository at this point in the history
feat: add ctx delete account action
  • Loading branch information
tymmesyde authored Jan 3, 2025
2 parents 9e8d555 + e0d777c commit 43e3384
Show file tree
Hide file tree
Showing 15 changed files with 227 additions and 18 deletions.
9 changes: 7 additions & 2 deletions src/models/ctx/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,10 +114,15 @@ impl<E: Env + 'static> Update<E> for Ctx {
self.status = CtxStatus::Loading(auth_request.to_owned());
Effects::one(authenticate::<E>(auth_request)).unchanged()
}
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Action(Action::Ctx(ActionCtx::Logout)) => {
Effects::msg(Msg::Internal(Internal::Logout(false))).unchanged()
}
Msg::Internal(Internal::Logout(deleted)) => {
let uid = self.profile.uid();
let session_effects = match self.profile.auth_key() {
Some(auth_key) => Effects::one(delete_session::<E>(auth_key)).unchanged(),
Some(auth_key) if !deleted => {
Effects::one(delete_session::<E>(auth_key)).unchanged()
}
_ => Effects::none().unchanged(),
};
let profile_effects =
Expand Down
2 changes: 1 addition & 1 deletion src/models/ctx/update_events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ pub fn update_events<E: Env + 'static>(
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Internal(Internal::Logout(_)) => {
let next_dismissed_events = DismissedEventsBucket::default();
*dismissed_events = next_dismissed_events;
Effects::msg(Msg::Internal(Internal::DismissedEventsChanged))
Expand Down
2 changes: 1 addition & 1 deletion src/models/ctx/update_library.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ pub fn update_library<E: Env + 'static>(
) -> Effects {
let auth_key = profile.auth_key();
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Internal(Internal::Logout(_)) => {
let next_library = LibraryBucket::default();
if *library != next_library {
*library = next_library;
Expand Down
2 changes: 1 addition & 1 deletion src/models/ctx/update_notifications.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ pub fn update_notifications<E: Env + 'static>(
Msg::Internal(Internal::DismissNotificationItem(id.to_owned())),
)
.unchanged(),
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Internal(Internal::Logout(_)) => {
let notification_catalogs_effects = eq_update(notification_catalogs, vec![]);
let next_notifications = NotificationsBucket::new::<E>(profile.uid(), vec![]);
let notifications_effects = if *notifications != next_notifications {
Expand Down
43 changes: 40 additions & 3 deletions src/models/ctx/update_profile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::types::addon::Descriptor;
use crate::types::api::{
fetch_api, APIError, APIRequest, APIResult, CollectionResponse, SuccessResponse,
};
use crate::types::profile::{Auth, AuthKey, Profile, Settings, User};
use crate::types::profile::{Auth, AuthKey, Password, Profile, Settings, User};
use crate::types::streams::StreamsBucket;

pub fn update_profile<E: Env + 'static>(
Expand All @@ -21,7 +21,7 @@ pub fn update_profile<E: Env + 'static>(
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Internal(Internal::Logout(_)) => {
let next_profile = Profile::default();
if *profile != next_profile {
*profile = next_profile;
Expand All @@ -30,6 +30,14 @@ pub fn update_profile<E: Env + 'static>(
Effects::none().unchanged()
}
}
Msg::Action(Action::Ctx(ActionCtx::DeleteAccount(password))) => match profile.auth_key() {
Some(auth_key) => Effects::one(delete_account::<E>(auth_key, password)).unchanged(),
_ => Effects::msg(Msg::Event(Event::Error {
error: CtxError::from(OtherError::UserNotLoggedIn),
source: Box::new(Event::UserAccountDeleted { uid: profile.uid() }),
}))
.unchanged(),
},
Msg::Action(Action::Ctx(ActionCtx::PushUserToAPI)) => match &profile.auth {
Some(Auth { key, user }) => {
Effects::one(push_user_to_api::<E>(user.to_owned(), key)).unchanged()
Expand Down Expand Up @@ -369,7 +377,7 @@ pub fn update_profile<E: Env + 'static>(
Err(error) => {
let session_expired_effects = match error {
CtxError::API(APIError { code, .. }) if *code == 1 => {
Effects::msg(Msg::Internal(Internal::Logout)).unchanged()
Effects::msg(Msg::Internal(Internal::Logout(false))).unchanged()
}
_ => Effects::none().unchanged(),
};
Expand All @@ -382,6 +390,17 @@ pub fn update_profile<E: Env + 'static>(
}
}
}
Msg::Internal(Internal::DeleteAccountAPIResult(
APIRequest::DeleteAccount { auth_key, .. },
result,
)) if profile.auth_key() == Some(auth_key) => match result {
Ok(_) => Effects::msg(Msg::Internal(Internal::Logout(true))).unchanged(),
Err(error) => Effects::msg(Msg::Event(Event::Error {
error: error.to_owned(),
source: Box::new(Event::UserAccountDeleted { uid: profile.uid() }),
}))
.unchanged(),
},
_ => Effects::none().unchanged(),
}
}
Expand Down Expand Up @@ -491,6 +510,24 @@ fn push_profile_to_storage<E: Env + 'static>(profile: &Profile) -> Effect {
.into()
}

fn delete_account<E: Env + 'static>(auth_key: &AuthKey, password: &Password) -> Effect {
let request = APIRequest::DeleteAccount {
auth_key: auth_key.to_owned(),
password: password.to_owned(),
};
EffectFuture::Concurrent(
fetch_api::<E, _, _, _>(&request)
.map_err(CtxError::from)
.and_then(|result| match result {
APIResult::Ok(result) => future::ok(result),
APIResult::Err(error) => future::err(CtxError::from(error)),
})
.map(move |result| Msg::Internal(Internal::DeleteAccountAPIResult(request, result)))
.boxed_env(),
)
.into()
}

fn addon_upgrade_error_effects(addon: &Descriptor, error: OtherError) -> Effects {
addon_action_error_effects(
error,
Expand Down
2 changes: 1 addition & 1 deletion src/models/ctx/update_search_history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub fn update_search_history<E: Env + 'static>(
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Internal(Internal::Logout(_)) => {
let next_search_history = SearchHistoryBucket::default();
*search_history = next_search_history;
Effects::msg(Msg::Internal(Internal::SearchHistoryChanged))
Expand Down
4 changes: 2 additions & 2 deletions src/models/ctx/update_streams.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use std::collections::hash_map::Entry;
use crate::constants::STREAMS_STORAGE_KEY;
use crate::models::common::{Loadable, ResourceLoadable};
use crate::models::ctx::{CtxError, CtxStatus};
use crate::runtime::msg::{Action, ActionCtx, CtxAuthResponse, Event, Internal, Msg};
use crate::runtime::msg::{CtxAuthResponse, Event, Internal, Msg};
use crate::runtime::{Effect, EffectFuture, Effects, Env, EnvFutureExt};
use crate::types::streams::{StreamsBucket, StreamsItem, StreamsItemKey};

Expand All @@ -15,7 +15,7 @@ pub fn update_streams<E: Env + 'static>(
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
Msg::Internal(Internal::Logout(_)) => {
let next_streams = StreamsBucket::default();
if *streams != next_streams {
*streams = next_streams;
Expand Down
4 changes: 1 addition & 3 deletions src/models/ctx/update_trakt_addon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ pub fn update_trakt_addon<E: Env + 'static>(
msg: &Msg,
) -> Effects {
match msg {
Msg::Action(Action::Ctx(ActionCtx::Logout)) | Msg::Internal(Internal::Logout) => {
eq_update(trakt_addon, None)
}
Msg::Internal(Internal::Logout(_)) => eq_update(trakt_addon, None),
Msg::Action(Action::Ctx(ActionCtx::InstallTraktAddon)) => {
Effects::msg(Msg::Internal(Internal::InstallTraktAddon))
}
Expand Down
2 changes: 2 additions & 0 deletions src/runtime/msg/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ use std::ops::Range;
use serde::Deserialize;
use url::Url;

use crate::types::profile::Password;
use crate::types::streams::StreamItemState;
use crate::{
models::{
Expand Down Expand Up @@ -34,6 +35,7 @@ use crate::{
pub enum ActionCtx {
Authenticate(AuthRequest),
Logout,
DeleteAccount(Password),
InstallAddon(Descriptor),
InstallTraktAddon,
LogoutTrakt,
Expand Down
3 changes: 3 additions & 0 deletions src/runtime/msg/event.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,9 @@ pub enum Event {
UserLoggedOut {
uid: UID,
},
UserAccountDeleted {
uid: UID,
},
SessionDeleted {
auth_key: AuthKey,
},
Expand Down
6 changes: 4 additions & 2 deletions src/runtime/msg/internal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,12 +49,14 @@ pub enum Internal {
AddonsAPIResult(APIRequest, Result<Vec<Descriptor>, CtxError>),
/// Result for pull user from API.
UserAPIResult(APIRequest, Result<User, CtxError>),
/// Result for deleting account from API.
DeleteAccountAPIResult(APIRequest, Result<SuccessResponse, CtxError>),
/// Result for library sync plan with API.
LibrarySyncPlanResult(DatastoreRequest, Result<LibraryPlanResponse, CtxError>),
/// Result for pull library items from API.
LibraryPullResult(DatastoreRequest, Result<Vec<LibraryItem>, CtxError>),
/// Dispatched when expired session is detected
Logout,
/// Dispatched when the user session needs to be cleared with a flag if the session was already deleted server-side
Logout(bool),
/// Internal event dispatched on user action or login
/// to install the addon if it's not present
InstallTraktAddon,
Expand Down
8 changes: 7 additions & 1 deletion src/types/api/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use core::fmt;
use crate::constants::{API_URL, LINK_API_URL};
use crate::types::addon::Descriptor;
use crate::types::library::LibraryItem;
use crate::types::profile::{AuthKey, GDPRConsent, User};
use crate::types::profile::{AuthKey, GDPRConsent, Password, User};
use crate::types::resource::SeriesInfo;
use chrono::{DateTime, Local};
#[cfg(test)]
Expand Down Expand Up @@ -50,6 +50,11 @@ pub enum APIRequest {
auth_key: AuthKey,
},
#[serde(rename_all = "camelCase")]
DeleteAccount {
auth_key: AuthKey,
password: Password,
},
#[serde(rename_all = "camelCase")]
AddonCollectionGet {
auth_key: AuthKey,
update: bool,
Expand Down Expand Up @@ -155,6 +160,7 @@ impl FetchRequestParams<APIRequest> for APIRequest {
APIRequest::Auth(AuthRequest::Facebook { .. }) => "authWithFacebook".to_owned(),
APIRequest::Auth(AuthRequest::Register { .. }) => "register".to_owned(),
APIRequest::Logout { .. } => "logout".to_owned(),
APIRequest::DeleteAccount { .. } => "deleteUser".to_owned(),
APIRequest::AddonCollectionGet { .. } => "addonCollectionGet".to_owned(),
APIRequest::AddonCollectionSet { .. } => "addonCollectionSet".to_owned(),
APIRequest::GetUser { .. } => "getUser".to_owned(),
Expand Down
18 changes: 17 additions & 1 deletion src/types/profile/auth.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::fmt::Display;
use std::fmt::{Debug, Display};

use crate::types::profile::User;
use serde::{Deserialize, Serialize};
Expand All @@ -19,3 +19,19 @@ pub struct Auth {
pub key: AuthKey,
pub user: User,
}

#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
#[cfg_attr(test, derive(Default))]
pub struct Password(pub String);

impl Display for Password {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

impl Debug for Password {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<SENSITIVE>>")
}
}
Loading

0 comments on commit 43e3384

Please sign in to comment.