diff --git a/app/app/auth/views/forgot_password.py b/app/app/auth/views/forgot_password.py index 684fd38..42246a1 100644 --- a/app/app/auth/views/forgot_password.py +++ b/app/app/auth/views/forgot_password.py @@ -1,4 +1,4 @@ -from flask import request, render_template, redirect, url_for, flash, g +from flask import request, render_template, flash, g from flask_wtf import FlaskForm from wtforms import StringField, validators @@ -16,7 +16,7 @@ class ForgotPasswordForm(FlaskForm): @auth_bp.route("/forgot_password", methods=["GET", "POST"]) @limiter.limit( - "10/minute", deduct_when=lambda r: hasattr(g, "deduct_limit") and g.deduct_limit + "10/hour", deduct_when=lambda r: hasattr(g, "deduct_limit") and g.deduct_limit ) def forgot_password(): form = ForgotPasswordForm(request.form) @@ -37,6 +37,5 @@ def forgot_password(): if user: LOG.d("Send forgot password email to %s", user) send_reset_password_email(user) - return redirect(url_for("auth.forgot_password")) return render_template("auth/forgot_password.html", form=form) diff --git a/app/app/auth/views/reset_password.py b/app/app/auth/views/reset_password.py index d3e9f74..e331cd9 100644 --- a/app/app/auth/views/reset_password.py +++ b/app/app/auth/views/reset_password.py @@ -60,8 +60,8 @@ def reset_password(): # this can be served to activate user too user.activated = True - # remove the reset password code - ResetPasswordCode.delete(reset_password_code.id) + # remove all reset password codes + ResetPasswordCode.filter_by(user_id=user.id).delete() # change the alternative_id to log user out on other browsers user.alternative_id = str(uuid.uuid4()) diff --git a/app/app/dashboard/views/setting.py b/app/app/dashboard/views/setting.py index 7ad4899..79ccb16 100644 --- a/app/app/dashboard/views/setting.py +++ b/app/app/dashboard/views/setting.py @@ -198,6 +198,16 @@ def setting(): ) return redirect(url_for("dashboard.setting")) + if current_user.profile_picture_id is not None: + current_profile_file = File.get_by( + id=current_user.profile_picture_id + ) + if ( + current_profile_file is not None + and current_profile_file.user_id == current_user.id + ): + s3.delete(current_user.path) + file_path = random_string(30) file = File.create(user_id=current_user.id, path=file_path) @@ -451,8 +461,13 @@ def send_change_email_confirmation(user: User, email_change: EmailChange): @dashboard_bp.route("/resend_email_change", methods=["GET", "POST"]) +@limiter.limit("5/hour") @login_required def resend_email_change(): + form = CSRFValidationForm() + if not form.validate(): + flash("Invalid request. Please try again", "warning") + return redirect(url_for("dashboard.setting")) email_change = EmailChange.get_by(user_id=current_user.id) if email_change: # extend email change expiration @@ -472,6 +487,10 @@ def resend_email_change(): @dashboard_bp.route("/cancel_email_change", methods=["GET", "POST"]) @login_required def cancel_email_change(): + form = CSRFValidationForm() + if not form.validate(): + flash("Invalid request. Please try again", "warning") + return redirect(url_for("dashboard.setting")) email_change = EmailChange.get_by(user_id=current_user.id) if email_change: EmailChange.delete(email_change.id) diff --git a/app/app/models.py b/app/app/models.py index 76a71b5..7a1ad0a 100644 --- a/app/app/models.py +++ b/app/app/models.py @@ -580,19 +580,6 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): Session.flush() user.default_mailbox_id = mb.id - # create a first alias mail to show user how to use when they login - alias = Alias.create_new( - user, - prefix="simplelogin-newsletter", - mailbox_id=mb.id, - note="This is your first alias. It's used to receive SimpleLogin communications " - "like new features announcements, newsletters.", - ) - Session.flush() - - user.newsletter_alias_id = alias.id - Session.flush() - # generate an alternative_id if needed if "alternative_id" not in kwargs: user.alternative_id = str(uuid.uuid4()) @@ -611,6 +598,19 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): Session.flush() return user + # create a first alias mail to show user how to use when they login + alias = Alias.create_new( + user, + prefix="simplelogin-newsletter", + mailbox_id=mb.id, + note="This is your first alias. It's used to receive SimpleLogin communications " + "like new features announcements, newsletters.", + ) + Session.flush() + + user.newsletter_alias_id = alias.id + Session.flush() + if config.DISABLE_ONBOARDING: LOG.d("Disable onboarding emails") return user @@ -636,7 +636,7 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): return user def get_active_subscription( - self, + self, include_partner_subscription: bool = True ) -> Optional[ Union[ Subscription @@ -664,19 +664,24 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): if coinbase_subscription and coinbase_subscription.is_active(): return coinbase_subscription - partner_sub: PartnerSubscription = PartnerSubscription.find_by_user_id(self.id) - if partner_sub and partner_sub.is_active(): - return partner_sub + if include_partner_subscription: + partner_sub: PartnerSubscription = PartnerSubscription.find_by_user_id( + self.id + ) + if partner_sub and partner_sub.is_active(): + return partner_sub return None # region Billing - def lifetime_or_active_subscription(self) -> bool: + def lifetime_or_active_subscription( + self, include_partner_subscription: bool = True + ) -> bool: """True if user has lifetime licence or active subscription""" if self.lifetime: return True - return self.get_active_subscription() is not None + return self.get_active_subscription(include_partner_subscription) is not None def is_paid(self) -> bool: """same as _lifetime_or_active_subscription but not include free manual subscription""" @@ -705,14 +710,14 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle): return True - def is_premium(self) -> bool: + def is_premium(self, include_partner_subscription: bool = True) -> bool: """ user is premium if they: - have a lifetime deal or - in trial period or - active subscription """ - if self.lifetime_or_active_subscription(): + if self.lifetime_or_active_subscription(include_partner_subscription): return True if self.trial_end and arrow.now() < self.trial_end: diff --git a/app/templates/dashboard/setting.html b/app/templates/dashboard/setting.html index 00a781f..ad4b7ee 100644 --- a/app/templates/dashboard/setting.html +++ b/app/templates/dashboard/setting.html @@ -181,10 +181,10 @@