4.64.3
Some checks failed
Build-Release-Image / Build-Image (linux/amd64) (push) Successful in 3m8s
Build-Release-Image / Build-Image (linux/arm64) (push) Failing after 15m37s
Build-Release-Image / Merge-Images (push) Has been skipped
Build-Release-Image / Create-Release (push) Has been skipped
Build-Release-Image / Notify (push) Has been skipped

This commit is contained in:
MrMeeb 2025-01-27 12:00:07 +00:00
parent ed37325b32
commit e47e5a5255
9 changed files with 59 additions and 19 deletions

View File

@ -144,5 +144,6 @@ def get_available_domains_for_random_alias_v2():
@require_api_auth @require_api_auth
def unlink_proton_account(): def unlink_proton_account():
user = g.user user = g.user
perform_proton_account_unlink(user) if not perform_proton_account_unlink(user):
return jsonify(error="The account cannot be unlinked"), 400
return jsonify({"ok": True}) return jsonify({"ok": True})

View File

@ -239,6 +239,8 @@ def unlink_proton_account():
flash("Invalid request", "warning") flash("Invalid request", "warning")
return redirect(url_for("dashboard.setting")) return redirect(url_for("dashboard.setting"))
perform_proton_account_unlink(current_user) if not perform_proton_account_unlink(current_user):
flash("Your Proton account has been unlinked", "success") flash("Account cannot be unlinked", "warning")
else:
flash("Your Proton account has been unlinked", "success")
return redirect(url_for("dashboard.setting")) return redirect(url_for("dashboard.setting"))

View File

@ -41,7 +41,7 @@ from app.models import (
PartnerSubscription, PartnerSubscription,
UnsubscribeBehaviourEnum, UnsubscribeBehaviourEnum,
) )
from app.proton.utils import get_proton_partner from app.proton.utils import get_proton_partner, can_unlink_proton_account
from app.utils import ( from app.utils import (
random_string, random_string,
CSRFValidationForm, CSRFValidationForm,
@ -323,4 +323,5 @@ def setting():
ALIAS_RAND_SUFFIX_LENGTH=ALIAS_RANDOM_SUFFIX_LENGTH, ALIAS_RAND_SUFFIX_LENGTH=ALIAS_RANDOM_SUFFIX_LENGTH,
connect_with_proton=CONNECT_WITH_PROTON, connect_with_proton=CONNECT_WITH_PROTON,
proton_linked_account=proton_linked_account, proton_linked_account=proton_linked_account,
can_unlink_proton_account=can_unlink_proton_account(current_user),
) )

View File

@ -33,8 +33,11 @@ from app.models import (
SLDomain, SLDomain,
Hibp, Hibp,
AliasHibp, AliasHibp,
PartnerUser,
PartnerSubscription,
) )
from app.pgp_utils import load_public_key from app.pgp_utils import load_public_key
from app.proton.utils import get_proton_partner
def fake_data(): def fake_data():
@ -269,3 +272,27 @@ def fake_data():
CustomDomain.create( CustomDomain.create(
user_id=user.id, domain="old.com", verified=True, ownership_verified=True user_id=user.id, domain="old.com", verified=True, ownership_verified=True
) )
# Create a user
proton_partner = get_proton_partner()
user = User.create(
email="test@proton.me",
name="Proton test",
password="password",
activated=True,
is_admin=False,
intro_shown=True,
from_partner=True,
flush=True,
)
pu = PartnerUser.create(
user_id=user.id,
partner_id=proton_partner.id,
partner_email="test@proton.me",
external_user_id="DUMMY",
flush=True,
)
PartnerSubscription.create(
partner_user_id=pu.id, end_at=arrow.now().shift(years=1, days=1)
)
Session.commit()

View File

@ -1,9 +1,10 @@
from newrelic import agent
from typing import Optional from typing import Optional
from newrelic import agent
from app.db import Session from app.db import Session
from app.log import LOG
from app.errors import ProtonPartnerNotSetUp from app.errors import ProtonPartnerNotSetUp
from app.log import LOG
from app.models import Partner, PartnerUser, User from app.models import Partner, PartnerUser, User
from app.user_audit_log_utils import emit_user_audit_log, UserAuditLogAction from app.user_audit_log_utils import emit_user_audit_log, UserAuditLogAction
@ -26,7 +27,13 @@ def is_proton_partner(partner: Partner) -> bool:
return partner.name == PROTON_PARTNER_NAME return partner.name == PROTON_PARTNER_NAME
def perform_proton_account_unlink(current_user: User): def can_unlink_proton_account(user: User) -> bool:
return (user.flags & User.FLAG_CREATED_FROM_PARTNER) == 0
def perform_proton_account_unlink(current_user: User) -> bool:
if not can_unlink_proton_account(current_user):
return False
proton_partner = get_proton_partner() proton_partner = get_proton_partner()
partner_user = PartnerUser.get_by( partner_user = PartnerUser.get_by(
user_id=current_user.id, partner_id=proton_partner.id user_id=current_user.id, partner_id=proton_partner.id
@ -41,3 +48,4 @@ def perform_proton_account_unlink(current_user: User):
PartnerUser.delete(partner_user.id) PartnerUser.delete(partner_user.id)
Session.commit() Session.commit()
agent.record_custom_event("AccountUnlinked", {"partner": proton_partner.name}) agent.record_custom_event("AccountUnlinked", {"partner": proton_partner.name})
return True

View File

@ -47,11 +47,6 @@ from typing import List, Tuple, Optional
import newrelic.agent import newrelic.agent
from aiosmtpd.controller import Controller from aiosmtpd.controller import Controller
from aiosmtpd.smtp import Envelope from aiosmtpd.smtp import Envelope
from email_validator import validate_email, EmailNotValidError
from flanker.addresslib import address
from flanker.addresslib.address import EmailAddress
from sqlalchemy.exc import IntegrityError
from app import pgp_utils, s3, config, contact_utils from app import pgp_utils, s3, config, contact_utils
from app.alias_utils import ( from app.alias_utils import (
try_auto_create, try_auto_create,
@ -174,8 +169,12 @@ from app.pgp_utils import (
load_public_key_and_check, load_public_key_and_check,
) )
from app.utils import sanitize_email from app.utils import sanitize_email
from email_validator import validate_email, EmailNotValidError
from flanker.addresslib import address
from flanker.addresslib.address import EmailAddress
from init_app import load_pgp_public_keys from init_app import load_pgp_public_keys
from server import create_light_app from server import create_light_app
from sqlalchemy.exc import IntegrityError
def get_or_create_contact( def get_or_create_contact(
@ -606,9 +605,11 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str
if reply_to_email == alias.email: if reply_to_email == alias.email:
LOG.i("Reply-to same as alias %s", alias) LOG.i("Reply-to same as alias %s", alias)
else: else:
reply_to_contact.append( reply_contact = get_or_create_reply_to_contact(
get_or_create_reply_to_contact(reply_to_email, alias, msg) reply_to_email, alias, msg
) )
if reply_contact:
reply_to_contact.append(reply_contact)
if alias.user.delete_on is not None: if alias.user.delete_on is not None:
LOG.d(f"user {user} is pending to be deleted. Do not forward") LOG.d(f"user {user} is pending to be deleted. Do not forward")

View File

@ -56,14 +56,15 @@ def add_sl_domains():
Session.commit() Session.commit()
def add_proton_partner(): def add_proton_partner() -> Partner:
proton_partner = Partner.get_by(name=PROTON_PARTNER_NAME) proton_partner = Partner.get_by(name=PROTON_PARTNER_NAME)
if not proton_partner: if not proton_partner:
Partner.create( proton_partner = Partner.create(
name=PROTON_PARTNER_NAME, name=PROTON_PARTNER_NAME,
contact_email="simplelogin@protonmail.com", contact_email="simplelogin@protonmail.com",
) )
Session.commit() Session.commit()
return proton_partner
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -8,7 +8,6 @@ import flask_limiter
import flask_profiler import flask_profiler
import newrelic.agent import newrelic.agent
import sentry_sdk import sentry_sdk
from flask import ( from flask import (
Flask, Flask,
redirect, redirect,
@ -498,9 +497,9 @@ def register_custom_commands(app):
from init_app import add_sl_domains, add_proton_partner from init_app import add_sl_domains, add_proton_partner
LOG.w("reset db, add fake data") LOG.w("reset db, add fake data")
add_proton_partner()
fake_data() fake_data()
add_sl_domains() add_sl_domains()
add_proton_partner()
@app.cli.command("send-newsletter") @app.cli.command("send-newsletter")
@click.option("-n", "--newsletter_id", type=int, help="Newsletter ID to be sent") @click.option("-n", "--newsletter_id", type=int, help="Newsletter ID to be sent")

View File

@ -144,7 +144,7 @@
</div> </div>
<!-- END change name & profile picture --> <!-- END change name & profile picture -->
<!-- Connect with Proton --> <!-- Connect with Proton -->
{% if connect_with_proton %} {% if connect_with_proton and can_unlink_proton_account %}
<div class="card" id="connect-with-proton"> <div class="card" id="connect-with-proton">
<div class="card-body"> <div class="card-body">