From 32fc629f57e470e20e65f4bf33cc87656a214e96 Mon Sep 17 00:00:00 2001 From: Lucca Dukic <109136188+LuccaBitfly@users.noreply.github.com> Date: Tue, 21 Jan 2025 14:45:15 +0100 Subject: [PATCH 1/3] fix: don't allow deletion of user with active subscription --- handlers/user.go | 74 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 53 insertions(+), 21 deletions(-) diff --git a/handlers/user.go b/handlers/user.go index 7d6c09f00e..630ec04451 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -975,38 +975,70 @@ func UserAuthorizeConfirmPost(w http.ResponseWriter, r *http.Request) { } } +func getHasUserActiveSubscription(userId uint64) (bool, error) { + var hasUserActiveSubscription bool + err := db.FrontendReaderDB.Get(&hasUserActiveSubscription, ` + SELECT EXISTS ( + SELECT uss.price_id + FROM users_stripe_subscriptions uss + LEFT JOIN users u ON u.stripe_customer_id = uss.customer_id + WHERE uss.active = true AND u.id = $1 + + UNION + + SELECT product_id + FROM users_app_subscriptions uas + LEFT JOIN users u ON u.id = uas.user_id + WHERE uas.active = true AND u.id = $1 + )`, userId) + if err != nil { + return false, err + } + return hasUserActiveSubscription, nil +} + func UserDeletePost(w http.ResponseWriter, r *http.Request) { logger := logger.WithField("route", r.URL.String()) - user, session, err := getUserSession(r) + user, _, err := getUserSession(r) if err != nil { logger.Errorf("error retrieving session: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) return } - if user.Authenticated { - err := db.DeleteUserById(user.UserID) - if err != nil { - logger.Errorf("error deleting user by email for user: %v %v", user.UserID, err) - http.Redirect(w, r, "/user/settings", http.StatusSeeOther) - utils.SetFlash(w, r, "", "Error: Could not delete user.") - session.Save(r, w) - http.Redirect(w, r, "/user/settings", http.StatusSeeOther) - return - } - - Logout(w, r) - err = purgeAllSessionsForUser(r.Context(), user.UserID) - if err != nil { - utils.LogError(err, "error purging sessions for user", 0, map[string]interface{}{"userID": user.UserID}) - utils.SetFlash(w, r, authSessionName, authInternalServerErrorFlashMsg) - http.Redirect(w, r, "/login", http.StatusSeeOther) - return - } - } else { + if !user.Authenticated { utils.LogError(nil, "Trying to delete an unauthenticated user", 0) http.Redirect(w, r, "/user/settings", http.StatusSeeOther) return } + // don't allow user to delete account if they have an active subscription + hasUserActiveSubscription, err := getHasUserActiveSubscription(user.UserID) + if err != nil { + logger.Errorf("error checking if user has active subscription: %v", err) + http.Error(w, "Internal server error", http.StatusInternalServerError) + return + } + if hasUserActiveSubscription { + utils.SetFlash(w, r, authSessionName, "Error: You cannot delete your account while you have an active subscription.") + http.Redirect(w, r, "/user/settings", http.StatusSeeOther) + return + } + + err = db.DeleteUserById(user.UserID) + if err != nil { + logger.Errorf("error deleting user by email for user: %v %v", user.UserID, err) + utils.SetFlash(w, r, authSessionName, "Error: Could not delete user.") + http.Redirect(w, r, "/user/settings", http.StatusSeeOther) + return + } + + Logout(w, r) + err = purgeAllSessionsForUser(r.Context(), user.UserID) + if err != nil { + utils.LogError(err, "error purging sessions for user", 0, map[string]interface{}{"userID": user.UserID}) + utils.SetFlash(w, r, authSessionName, authInternalServerErrorFlashMsg) + http.Redirect(w, r, "/login", http.StatusSeeOther) + return + } } func UserUpdateFlagsPost(w http.ResponseWriter, r *http.Request) { From 354bc3d2b24168f47cd07c7378ac0c1af11f9f73 Mon Sep 17 00:00:00 2001 From: Lucca Dukic <109136188+LuccaBitfly@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:07:48 +0100 Subject: [PATCH 2/3] feat: disable delete button for active subscription users --- handlers/user.go | 10 ++++++++++ templates/user/settings.html | 10 +++++++--- types/templates.go | 17 +++++++++-------- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/handlers/user.go b/handlers/user.go index 630ec04451..cb8ee9290a 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -115,6 +115,16 @@ func UserSettings(w http.ResponseWriter, r *http.Request) { } } + // disable delete button if user has active subscription + hasUserActiveSubscription, err := getHasUserActiveSubscription(user.UserID) + if err != nil { + logger.Errorf("Error retrieving the active subscription for user: %v %v", user.UserID, err) + utils.SetFlash(w, r, "", "Error: Something went wrong.") + http.Redirect(w, r, "/user/settings", http.StatusSeeOther) + return + } + userSettingsData.IsUserDeleteDisabled = hasUserActiveSubscription + userSettingsData.ApiStatistics.MaxDaily = &maxDaily userSettingsData.ApiStatistics.MaxMonthly = &maxMonthly diff --git a/templates/user/settings.html b/templates/user/settings.html index 2805989387..9276510118 100644 --- a/templates/user/settings.html +++ b/templates/user/settings.html @@ -460,9 +460,13 @@

Delete Account
- Warning, you will not be able to recover your account! - - + {{ if .IsUserDeleteDisabled }} + Warning, you will not be able to recover your account! + + + {{ else }} + You currently have an active premium subscription or premium API plan. To delete your account please cancel your subscription or contact our support + {{ end }}
diff --git a/types/templates.go b/types/templates.go index 7dd15e24bd..708ada2fcc 100644 --- a/types/templates.go +++ b/types/templates.go @@ -1220,14 +1220,15 @@ type CsrfData struct { type UserSettingsPageData struct { CsrfField template.HTML AuthData - Subscription UserSubscription - Premium UserPremiumSubscription - PairedDevices []PairedDevice - Sapphire *string - Emerald *string - Diamond *string - ShareMonitoringData bool - ApiStatistics *ApiStatistics + Subscription UserSubscription + Premium UserPremiumSubscription + PairedDevices []PairedDevice + Sapphire *string + Emerald *string + Diamond *string + ShareMonitoringData bool + ApiStatistics *ApiStatistics + IsUserDeleteDisabled bool } type PairedDevice struct { From 5316074e08002f9656745321d2de066ef6c2504d Mon Sep 17 00:00:00 2001 From: Lucca Dukic <109136188+LuccaBitfly@users.noreply.github.com> Date: Wed, 22 Jan 2025 10:16:32 +0100 Subject: [PATCH 3/3] fix: show correct error message when deleting user --- handlers/user.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/handlers/user.go b/handlers/user.go index cb8ee9290a..c632304241 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -1035,7 +1035,7 @@ func UserDeletePost(w http.ResponseWriter, r *http.Request) { err = db.DeleteUserById(user.UserID) if err != nil { - logger.Errorf("error deleting user by email for user: %v %v", user.UserID, err) + logger.Errorf("error deleting user by id for user: %v %v", user.UserID, err) utils.SetFlash(w, r, authSessionName, "Error: Could not delete user.") http.Redirect(w, r, "/user/settings", http.StatusSeeOther) return