Compare commits
2 Commits
Author | SHA1 | Date | |
---|---|---|---|
01dba12ed0 | |||
c872d43c3d |
@ -84,7 +84,7 @@ For email gurus, we have chosen 1024 key length instead of 2048 for DNS simplici
|
||||
|
||||
### DNS
|
||||
|
||||
Please note that DNS changes could take up to 24 hours to propagate. In practice, it's a lot faster though (~1 minute or so in our test). In DNS setup, we usually use domain with a trailing dot (`.`) at the end to to force using absolute domain.
|
||||
Please note that DNS changes could take up to 24 hours to propagate. In practice, it's a lot faster though (~1 minute or so in our test). In DNS setup, we usually use domain with a trailing dot (`.`) at the end to force using absolute domain.
|
||||
|
||||
|
||||
#### MX record
|
||||
|
@ -6,6 +6,7 @@ from typing import Optional
|
||||
import arrow
|
||||
from arrow import Arrow
|
||||
from newrelic import agent
|
||||
from psycopg2.errors import UniqueViolation
|
||||
from sqlalchemy import or_
|
||||
|
||||
from app.db import Session
|
||||
@ -160,15 +161,55 @@ class ClientMergeStrategy(ABC):
|
||||
|
||||
class NewUserStrategy(ClientMergeStrategy):
|
||||
def process(self) -> LinkResult:
|
||||
# Will create a new SL User with a random password
|
||||
canonical_email = canonicalize_email(self.link_request.email)
|
||||
new_user = User.create(
|
||||
email=canonical_email,
|
||||
name=self.link_request.name,
|
||||
password=random_string(20),
|
||||
activated=True,
|
||||
from_partner=self.link_request.from_partner,
|
||||
try:
|
||||
# Will create a new SL User with a random password
|
||||
new_user = User.create(
|
||||
email=canonical_email,
|
||||
name=self.link_request.name,
|
||||
password=random_string(20),
|
||||
activated=True,
|
||||
from_partner=self.link_request.from_partner,
|
||||
)
|
||||
self.create_partner_user(new_user)
|
||||
Session.commit()
|
||||
|
||||
if not new_user.created_by_partner:
|
||||
send_welcome_email(new_user)
|
||||
|
||||
agent.record_custom_event(
|
||||
"PartnerUserCreation", {"partner": self.partner.name}
|
||||
)
|
||||
|
||||
return LinkResult(
|
||||
user=new_user,
|
||||
strategy=self.__class__.__name__,
|
||||
)
|
||||
except UniqueViolation:
|
||||
return self.create_missing_link(canonical_email)
|
||||
|
||||
def create_missing_link(self, canonical_email: str):
|
||||
# If there's a unique key violation due to race conditions try to create only the partner if needed
|
||||
partner_user = PartnerUser.get_by(
|
||||
external_user_id=self.link_request.external_user_id,
|
||||
partner_id=self.partner.id,
|
||||
)
|
||||
if partner_user is None:
|
||||
# Get the user by canonical email and if not by normal email
|
||||
user = User.get_by(email=canonical_email) or User.get_by(
|
||||
email=self.link_request.email
|
||||
)
|
||||
if not user:
|
||||
raise RuntimeError(
|
||||
"Tried to create only partner on UniqueViolation but cannot find the user"
|
||||
)
|
||||
partner_user = self.create_partner_user(user)
|
||||
Session.commit()
|
||||
return LinkResult(
|
||||
user=partner_user.user, strategy=ExistingUnlinkedUserStrategy.__name__
|
||||
)
|
||||
|
||||
def create_partner_user(self, new_user: User):
|
||||
partner_user = create_partner_user(
|
||||
user=new_user,
|
||||
partner_id=self.partner.id,
|
||||
@ -182,17 +223,7 @@ class NewUserStrategy(ClientMergeStrategy):
|
||||
partner_user,
|
||||
self.link_request.plan,
|
||||
)
|
||||
Session.commit()
|
||||
|
||||
if not new_user.created_by_partner:
|
||||
send_welcome_email(new_user)
|
||||
|
||||
agent.record_custom_event("PartnerUserCreation", {"partner": self.partner.name})
|
||||
|
||||
return LinkResult(
|
||||
user=new_user,
|
||||
strategy=self.__class__.__name__,
|
||||
)
|
||||
return partner_user
|
||||
|
||||
|
||||
class ExistingUnlinkedUserStrategy(ClientMergeStrategy):
|
||||
|
@ -58,7 +58,7 @@ def verify_prefix_suffix(
|
||||
|
||||
# alias_domain must be either one of user custom domains or built-in domains
|
||||
if alias_domain not in user.available_alias_domains(alias_options=alias_options):
|
||||
LOG.e("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||
LOG.i("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||
return False
|
||||
|
||||
# SimpleLogin domain case:
|
||||
@ -75,17 +75,17 @@ def verify_prefix_suffix(
|
||||
and not config.DISABLE_ALIAS_SUFFIX
|
||||
):
|
||||
if not alias_domain_prefix.startswith("."):
|
||||
LOG.e("User %s submits a wrong alias suffix %s", user, alias_suffix)
|
||||
LOG.i("User %s submits a wrong alias suffix %s", user, alias_suffix)
|
||||
return False
|
||||
|
||||
else:
|
||||
if alias_domain not in user_custom_domains:
|
||||
if not config.DISABLE_ALIAS_SUFFIX:
|
||||
LOG.e("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||
LOG.i("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||
return False
|
||||
|
||||
if alias_domain not in available_sl_domains:
|
||||
LOG.e("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||
LOG.i("wrong alias suffix %s, user %s", alias_suffix, user)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
@ -153,7 +153,8 @@ def new_custom_alias_v3():
|
||||
if not isinstance(data, dict):
|
||||
return jsonify(error="request body does not follow the required format"), 400
|
||||
|
||||
alias_prefix = data.get("alias_prefix", "").strip().lower().replace(" ", "")
|
||||
alias_prefix_data = data.get("alias_prefix", "") or ""
|
||||
alias_prefix = alias_prefix_data.strip().lower().replace(" ", "")
|
||||
signed_suffix = data.get("signed_suffix", "") or ""
|
||||
signed_suffix = signed_suffix.strip()
|
||||
|
||||
|
@ -227,7 +227,10 @@ def alias_contact_manager(alias_id):
|
||||
|
||||
page = 0
|
||||
if request.args.get("page"):
|
||||
page = int(request.args.get("page"))
|
||||
try:
|
||||
page = int(request.args.get("page"))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
query = request.args.get("query") or ""
|
||||
|
||||
|
@ -71,7 +71,10 @@ def index():
|
||||
|
||||
page = 0
|
||||
if request.args.get("page"):
|
||||
page = int(request.args.get("page"))
|
||||
try:
|
||||
page = int(request.args.get("page"))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
highlight_alias_id = None
|
||||
if request.args.get("highlight_alias_id"):
|
||||
|
@ -43,7 +43,10 @@ def notification_route(notification_id):
|
||||
def notifications_route():
|
||||
page = 0
|
||||
if request.args.get("page"):
|
||||
page = int(request.args.get("page"))
|
||||
try:
|
||||
page = int(request.args.get("page"))
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
notifications = (
|
||||
Notification.filter_by(user_id=current_user.id)
|
||||
|
@ -158,6 +158,8 @@ class File(Base, ModelMixin):
|
||||
path = sa.Column(sa.String(128), unique=True, nullable=False)
|
||||
user_id = sa.Column(sa.ForeignKey("users.id", ondelete="cascade"), nullable=True)
|
||||
|
||||
__table_args__ = (sa.Index("ix_file_user_id", "user_id"),)
|
||||
|
||||
def get_url(self, expires_in=3600):
|
||||
return s3.get_url(self.path, expires_in)
|
||||
|
||||
@ -319,6 +321,8 @@ class HibpNotifiedAlias(Base, ModelMixin):
|
||||
|
||||
notified_at = sa.Column(ArrowType, default=arrow.utcnow, nullable=False)
|
||||
|
||||
__table_args__ = (sa.Index("ix_hibp_notified_alias_user_id", "user_id"),)
|
||||
|
||||
|
||||
class Fido(Base, ModelMixin):
|
||||
__tablename__ = "fido"
|
||||
@ -333,6 +337,8 @@ class Fido(Base, ModelMixin):
|
||||
name = sa.Column(sa.String(128), nullable=False, unique=False)
|
||||
user_id = sa.Column(sa.ForeignKey("users.id", ondelete="cascade"), nullable=True)
|
||||
|
||||
__table_args__ = (sa.Index("ix_fido_user_id", "user_id"),)
|
||||
|
||||
|
||||
class User(Base, ModelMixin, UserMixin, PasswordOracle):
|
||||
__tablename__ = "users"
|
||||
@ -565,6 +571,11 @@ class User(Base, ModelMixin, UserMixin, PasswordOracle):
|
||||
"ix_users_activated_trial_end_lifetime", activated, trial_end, lifetime
|
||||
),
|
||||
sa.Index("ix_users_delete_on", delete_on),
|
||||
sa.Index("ix_users_default_mailbox_id", default_mailbox_id),
|
||||
sa.Index(
|
||||
"ix_users_default_alias_custom_domain_id", default_alias_custom_domain_id
|
||||
),
|
||||
sa.Index("ix_users_profile_picture_id", profile_picture_id),
|
||||
)
|
||||
|
||||
@property
|
||||
@ -1221,6 +1232,8 @@ class ActivationCode(Base, ModelMixin):
|
||||
|
||||
expired = sa.Column(ArrowType, nullable=False, default=_expiration_1h)
|
||||
|
||||
__table_args__ = (sa.Index("ix_activation_code_user_id", "user_id"),)
|
||||
|
||||
def is_expired(self):
|
||||
return self.expired < arrow.now()
|
||||
|
||||
@ -1237,6 +1250,8 @@ class ResetPasswordCode(Base, ModelMixin):
|
||||
|
||||
expired = sa.Column(ArrowType, nullable=False, default=_expiration_1h)
|
||||
|
||||
__table_args__ = (sa.Index("ix_reset_password_code_user_id", "user_id"),)
|
||||
|
||||
def is_expired(self):
|
||||
return self.expired < arrow.now()
|
||||
|
||||
@ -1279,6 +1294,8 @@ class MfaBrowser(Base, ModelMixin):
|
||||
|
||||
user = orm.relationship(User)
|
||||
|
||||
__table_args__ = (sa.Index("ix_mfa_browser_user_id", "user_id"),)
|
||||
|
||||
@classmethod
|
||||
def create_new(cls, user, token_length=64) -> "MfaBrowser":
|
||||
found = False
|
||||
@ -1337,6 +1354,12 @@ class Client(Base, ModelMixin):
|
||||
user = orm.relationship(User)
|
||||
referral = orm.relationship("Referral")
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_client_user_id", "user_id"),
|
||||
sa.Index("ix_client_icon_id", "icon_id"),
|
||||
sa.Index("ix_client_referral_id", "referral_id"),
|
||||
)
|
||||
|
||||
def nb_user(self):
|
||||
return ClientUser.filter_by(client_id=self.id).count()
|
||||
|
||||
@ -1385,6 +1408,8 @@ class RedirectUri(Base, ModelMixin):
|
||||
|
||||
client = orm.relationship(Client, backref="redirect_uris")
|
||||
|
||||
__table_args__ = (sa.Index("ix_redirect_uri_client_id", "client_id"),)
|
||||
|
||||
|
||||
class AuthorizationCode(Base, ModelMixin):
|
||||
__tablename__ = "authorization_code"
|
||||
@ -1406,6 +1431,11 @@ class AuthorizationCode(Base, ModelMixin):
|
||||
|
||||
expired = sa.Column(ArrowType, nullable=False, default=_expiration_5m)
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_authorization_code_client_id", "client_id"),
|
||||
sa.Index("ix_authorization_code_user_id", "user_id"),
|
||||
)
|
||||
|
||||
def is_expired(self):
|
||||
return self.expired < arrow.now()
|
||||
|
||||
@ -1428,6 +1458,11 @@ class OauthToken(Base, ModelMixin):
|
||||
|
||||
expired = sa.Column(ArrowType, nullable=False, default=_expiration_1h)
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_oauth_token_user_id", "user_id"),
|
||||
sa.Index("ix_oauth_token_client_id", "client_id"),
|
||||
)
|
||||
|
||||
def is_expired(self):
|
||||
return self.expired < arrow.now()
|
||||
|
||||
@ -1581,6 +1616,7 @@ class Alias(Base, ModelMixin):
|
||||
postgresql_ops={"note": "gin_trgm_ops"},
|
||||
postgresql_using="gin",
|
||||
),
|
||||
Index("ix_alias_original_owner_id", "original_owner_id"),
|
||||
)
|
||||
|
||||
user = orm.relationship(User, foreign_keys=[user_id])
|
||||
@ -1666,6 +1702,11 @@ class Alias(Base, ModelMixin):
|
||||
custom_domain = Alias.get_custom_domain(email)
|
||||
if custom_domain:
|
||||
new_alias.custom_domain_id = custom_domain.id
|
||||
else:
|
||||
custom_domain = CustomDomain.get(kw["custom_domain_id"])
|
||||
# If it comes from a custom domain created from partner. Mark it as created from partner
|
||||
if custom_domain is not None and custom_domain.partner_id is not None:
|
||||
new_alias.flags = (new_alias.flags or 0) | Alias.FLAG_PARTNER_CREATED
|
||||
|
||||
Session.add(new_alias)
|
||||
DailyMetric.get_or_create_today_metric().nb_alias += 1
|
||||
@ -2069,7 +2110,12 @@ class Contact(Base, ModelMixin):
|
||||
|
||||
class EmailLog(Base, ModelMixin):
|
||||
__tablename__ = "email_log"
|
||||
__table_args__ = (Index("ix_email_log_created_at", "created_at"),)
|
||||
__table_args__ = (
|
||||
Index("ix_email_log_created_at", "created_at"),
|
||||
Index("ix_email_log_mailbox_id", "mailbox_id"),
|
||||
Index("ix_email_log_bounced_mailbox_id", "bounced_mailbox_id"),
|
||||
Index("ix_email_log_refused_email_id", "refused_email_id"),
|
||||
)
|
||||
|
||||
user_id = sa.Column(
|
||||
sa.ForeignKey(User.id, ondelete="cascade"), nullable=False, index=True
|
||||
@ -2345,6 +2391,7 @@ class AliasUsedOn(Base, ModelMixin):
|
||||
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint("alias_id", "hostname", name="uq_alias_used"),
|
||||
sa.Index("ix_alias_used_on_user_id", "user_id"),
|
||||
)
|
||||
|
||||
alias_id = sa.Column(
|
||||
@ -2371,6 +2418,11 @@ class ApiKey(Base, ModelMixin):
|
||||
|
||||
user = orm.relationship(User)
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_api_key_code", "code"),
|
||||
sa.Index("ix_api_key_user_id", "user_id"),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create(cls, user_id, name=None, **kwargs):
|
||||
code = random_string(60)
|
||||
@ -2529,6 +2581,7 @@ class AutoCreateRule(Base, ModelMixin):
|
||||
sa.UniqueConstraint(
|
||||
"custom_domain_id", "order", name="uq_auto_create_rule_order"
|
||||
),
|
||||
sa.Index("ix_auto_create_rule_custom_domain_id", "custom_domain_id"),
|
||||
)
|
||||
|
||||
custom_domain_id = sa.Column(
|
||||
@ -2572,6 +2625,7 @@ class DomainDeletedAlias(Base, ModelMixin):
|
||||
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint("domain_id", "email", name="uq_domain_trash"),
|
||||
sa.Index("ix_domain_deleted_alias_user_id", "user_id"),
|
||||
)
|
||||
|
||||
email = sa.Column(sa.String(256), nullable=False)
|
||||
@ -2632,6 +2686,8 @@ class Coupon(Base, ModelMixin):
|
||||
# a coupon can have an expiration
|
||||
expires_date = sa.Column(ArrowType, nullable=True)
|
||||
|
||||
__table_args__ = (sa.Index("ix_coupon_used_by_user_id", "used_by_user_id"),)
|
||||
|
||||
|
||||
class Directory(Base, ModelMixin):
|
||||
__tablename__ = "directory"
|
||||
@ -2646,6 +2702,8 @@ class Directory(Base, ModelMixin):
|
||||
"Mailbox", secondary="directory_mailbox", lazy="joined"
|
||||
)
|
||||
|
||||
__table_args__ = (sa.Index("ix_directory_user_id", "user_id"),)
|
||||
|
||||
@property
|
||||
def mailboxes(self):
|
||||
if self._mailboxes:
|
||||
@ -2747,7 +2805,10 @@ class Mailbox(Base, ModelMixin):
|
||||
|
||||
generic_subject = sa.Column(sa.String(78), nullable=True)
|
||||
|
||||
__table_args__ = (sa.UniqueConstraint("user_id", "email", name="uq_mailbox_user"),)
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint("user_id", "email", name="uq_mailbox_user"),
|
||||
sa.Index("ix_mailbox_pgp_finger_print", "pgp_finger_print"),
|
||||
)
|
||||
|
||||
user = orm.relationship(User, foreign_keys=[user_id])
|
||||
|
||||
@ -2884,6 +2945,8 @@ class RefusedEmail(Base, ModelMixin):
|
||||
# toggle this when email content (stored at full_report_path & path are deleted)
|
||||
deleted = sa.Column(sa.Boolean, nullable=False, default=False, server_default="0")
|
||||
|
||||
__table_args__ = (sa.Index("ix_refused_email_user_id", "user_id"),)
|
||||
|
||||
def get_url(self, expires_in=3600):
|
||||
if self.path:
|
||||
return s3.get_url(self.path, expires_in)
|
||||
@ -2906,6 +2969,8 @@ class Referral(Base, ModelMixin):
|
||||
|
||||
user = orm.relationship(User, foreign_keys=[user_id], backref="referrals")
|
||||
|
||||
__table_args__ = (sa.Index("ix_referral_user_id", "user_id"),)
|
||||
|
||||
@property
|
||||
def nb_user(self) -> int:
|
||||
return User.filter_by(referral_id=self.id, activated=True).count()
|
||||
@ -2945,6 +3010,8 @@ class SentAlert(Base, ModelMixin):
|
||||
to_email = sa.Column(sa.String(256), nullable=False)
|
||||
alert_type = sa.Column(sa.String(256), nullable=False)
|
||||
|
||||
__table_args__ = (sa.Index("ix_sent_alert_user_id", "user_id"),)
|
||||
|
||||
|
||||
class AliasMailbox(Base, ModelMixin):
|
||||
__tablename__ = "alias_mailbox"
|
||||
@ -3190,6 +3257,11 @@ class BatchImport(Base, ModelMixin):
|
||||
file = orm.relationship(File)
|
||||
user = orm.relationship(User)
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_batch_import_file_id", "file_id"),
|
||||
sa.Index("ix_batch_import_user_id", "user_id"),
|
||||
)
|
||||
|
||||
def nb_alias(self):
|
||||
return Alias.filter_by(batch_import_id=self.id).count()
|
||||
|
||||
@ -3210,6 +3282,7 @@ class AuthorizedAddress(Base, ModelMixin):
|
||||
|
||||
__table_args__ = (
|
||||
sa.UniqueConstraint("mailbox_id", "email", name="uq_authorize_address"),
|
||||
sa.Index("ix_authorized_address_user_id", "user_id"),
|
||||
)
|
||||
|
||||
mailbox = orm.relationship(Mailbox, backref="authorized_addresses")
|
||||
@ -3351,6 +3424,8 @@ class Payout(Base, ModelMixin):
|
||||
|
||||
user = orm.relationship(User)
|
||||
|
||||
__table_args__ = (sa.Index("ix_payout_user_id", "user_id"),)
|
||||
|
||||
|
||||
class IgnoredEmail(Base, ModelMixin):
|
||||
"""If an email has mail_from and rcpt_to present in this table, discard it by returning 250 status."""
|
||||
@ -3452,6 +3527,8 @@ class PhoneReservation(Base, ModelMixin):
|
||||
start = sa.Column(ArrowType, nullable=False)
|
||||
end = sa.Column(ArrowType, nullable=False)
|
||||
|
||||
__table_args__ = (sa.Index("ix_phone_reservation_user_id", "user_id"),)
|
||||
|
||||
|
||||
class PhoneMessage(Base, ModelMixin):
|
||||
__tablename__ = "phone_message"
|
||||
@ -3626,6 +3703,11 @@ class ProviderComplaint(Base, ModelMixin):
|
||||
user = orm.relationship(User, foreign_keys=[user_id])
|
||||
refused_email = orm.relationship(RefusedEmail, foreign_keys=[refused_email_id])
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_provider_complaint_user_id", "user_id"),
|
||||
sa.Index("ix_provider_complaint_refused_email_id", "refused_email_id"),
|
||||
)
|
||||
|
||||
|
||||
class PartnerApiToken(Base, ModelMixin):
|
||||
__tablename__ = "partner_api_token"
|
||||
@ -3749,6 +3831,8 @@ class NewsletterUser(Base, ModelMixin):
|
||||
user = orm.relationship(User)
|
||||
newsletter = orm.relationship(Newsletter)
|
||||
|
||||
__table_args__ = (sa.Index("ix_newsletter_user_user_id", "user_id"),)
|
||||
|
||||
|
||||
class ApiToCookieToken(Base, ModelMixin):
|
||||
__tablename__ = "api_cookie_token"
|
||||
@ -3759,6 +3843,11 @@ class ApiToCookieToken(Base, ModelMixin):
|
||||
user = orm.relationship(User)
|
||||
api_key = orm.relationship(ApiKey)
|
||||
|
||||
__table_args__ = (
|
||||
sa.Index("ix_api_to_cookie_token_api_key_id", "api_key_id"),
|
||||
sa.Index("ix_api_to_cookie_token_user_id", "user_id"),
|
||||
)
|
||||
|
||||
@classmethod
|
||||
def create(cls, **kwargs):
|
||||
code = secrets.token_urlsafe(32)
|
||||
|
@ -7000,7 +7000,6 @@ unfunded
|
||||
unglazed
|
||||
ungloved
|
||||
unglue
|
||||
ungodly
|
||||
ungraded
|
||||
ungreased
|
||||
unguarded
|
||||
|
@ -0,0 +1,30 @@
|
||||
"""add missing indices on user and mailbox
|
||||
|
||||
Revision ID: 842ac670096e
|
||||
Revises: bc9aa210efa3
|
||||
Create Date: 2024-11-13 15:55:28.798506
|
||||
|
||||
"""
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '842ac670096e'
|
||||
down_revision = 'bc9aa210efa3'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.get_context().autocommit_block():
|
||||
op.create_index('ix_mailbox_pgp_finger_print', 'mailbox', ['pgp_finger_print'], unique=False)
|
||||
op.create_index('ix_users_default_mailbox_id', 'users', ['default_mailbox_id'], unique=False)
|
||||
# ### end Alembic commands ###
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.get_context().autocommit_block():
|
||||
op.drop_index('ix_users_default_mailbox_id', table_name='users')
|
||||
op.drop_index('ix_mailbox_pgp_finger_print', table_name='mailbox')
|
@ -0,0 +1,29 @@
|
||||
"""add missing indices on email log
|
||||
|
||||
Revision ID: 12274da2299f
|
||||
Revises: 842ac670096e
|
||||
Create Date: 2024-11-14 10:27:20.371191
|
||||
|
||||
"""
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '12274da2299f'
|
||||
down_revision = '842ac670096e'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.get_context().autocommit_block():
|
||||
op.create_index('ix_email_log_bounced_mailbox_id', 'email_log', ['bounced_mailbox_id'], unique=False)
|
||||
op.create_index('ix_email_log_mailbox_id', 'email_log', ['mailbox_id'], unique=False)
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.get_context().autocommit_block():
|
||||
op.drop_index('ix_email_log_mailbox_id', table_name='email_log')
|
||||
op.drop_index('ix_email_log_bounced_mailbox_id', table_name='email_log')
|
@ -0,0 +1,102 @@
|
||||
"""add missing indices for fk constraints
|
||||
|
||||
Revision ID: 0f3ee15b0014
|
||||
Revises: 12274da2299f
|
||||
Create Date: 2024-11-15 12:29:10.739938
|
||||
|
||||
"""
|
||||
import sqlalchemy_utils
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '0f3ee15b0014'
|
||||
down_revision = '12274da2299f'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
with op.get_context().autocommit_block():
|
||||
op.create_index('ix_activation_code_user_id', 'activation_code', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_alias_original_owner_id', 'alias', ['original_owner_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_alias_used_on_user_id', 'alias_used_on', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_api_to_cookie_token_api_key_id', 'api_cookie_token', ['api_key_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_api_to_cookie_token_user_id', 'api_cookie_token', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_api_key_code', 'api_key', ['code'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_api_key_user_id', 'api_key', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_authorization_code_client_id', 'authorization_code', ['client_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_authorization_code_user_id', 'authorization_code', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_authorized_address_user_id', 'authorized_address', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_auto_create_rule_custom_domain_id', 'auto_create_rule', ['custom_domain_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_batch_import_file_id', 'batch_import', ['file_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_batch_import_user_id', 'batch_import', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_client_icon_id', 'client', ['icon_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_client_referral_id', 'client', ['referral_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_client_user_id', 'client', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_coupon_used_by_user_id', 'coupon', ['used_by_user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_directory_user_id', 'directory', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_domain_deleted_alias_user_id', 'domain_deleted_alias', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_email_log_refused_email_id', 'email_log', ['refused_email_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_fido_user_id', 'fido', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_file_user_id', 'file', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_hibp_notified_alias_user_id', 'hibp_notified_alias', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_mfa_browser_user_id', 'mfa_browser', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_newsletter_user_user_id', 'newsletter_user', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_oauth_token_client_id', 'oauth_token', ['client_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_oauth_token_user_id', 'oauth_token', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_payout_user_id', 'payout', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_phone_reservation_user_id', 'phone_reservation', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_provider_complaint_refused_email_id', 'provider_complaint', ['refused_email_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_provider_complaint_user_id', 'provider_complaint', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_redirect_uri_client_id', 'redirect_uri', ['client_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_referral_user_id', 'referral', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_refused_email_user_id', 'refused_email', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_reset_password_code_user_id', 'reset_password_code', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_sent_alert_user_id', 'sent_alert', ['user_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_users_default_alias_custom_domain_id', 'users', ['default_alias_custom_domain_id'], unique=False, postgresql_concurrently=True)
|
||||
op.create_index('ix_users_profile_picture_id', 'users', ['profile_picture_id'], unique=False, postgresql_concurrently=True)
|
||||
|
||||
|
||||
|
||||
def downgrade():
|
||||
with op.get_context().autocommit_block():
|
||||
op.drop_index('ix_users_profile_picture_id', table_name='users')
|
||||
op.drop_index('ix_users_default_alias_custom_domain_id', table_name='users')
|
||||
op.drop_index('ix_sent_alert_user_id', table_name='sent_alert')
|
||||
op.drop_index('ix_reset_password_code_user_id', table_name='reset_password_code')
|
||||
op.drop_index('ix_refused_email_user_id', table_name='refused_email')
|
||||
op.drop_index('ix_referral_user_id', table_name='referral')
|
||||
op.drop_index('ix_redirect_uri_client_id', table_name='redirect_uri')
|
||||
op.drop_index('ix_provider_complaint_user_id', table_name='provider_complaint')
|
||||
op.drop_index('ix_provider_complaint_refused_email_id', table_name='provider_complaint')
|
||||
op.drop_index('ix_phone_reservation_user_id', table_name='phone_reservation')
|
||||
op.drop_index('ix_payout_user_id', table_name='payout')
|
||||
op.drop_index('ix_oauth_token_user_id', table_name='oauth_token')
|
||||
op.drop_index('ix_oauth_token_client_id', table_name='oauth_token')
|
||||
op.drop_index('ix_newsletter_user_user_id', table_name='newsletter_user')
|
||||
op.drop_index('ix_mfa_browser_user_id', table_name='mfa_browser')
|
||||
op.drop_index('ix_hibp_notified_alias_user_id', table_name='hibp_notified_alias')
|
||||
op.drop_index('ix_file_user_id', table_name='file')
|
||||
op.drop_index('ix_fido_user_id', table_name='fido')
|
||||
op.drop_index('ix_email_log_refused_email_id', table_name='email_log')
|
||||
op.drop_index('ix_domain_deleted_alias_user_id', table_name='domain_deleted_alias')
|
||||
op.drop_index('ix_directory_user_id', table_name='directory')
|
||||
op.drop_index('ix_coupon_used_by_user_id', table_name='coupon')
|
||||
op.drop_index('ix_client_user_id', table_name='client')
|
||||
op.drop_index('ix_client_referral_id', table_name='client')
|
||||
op.drop_index('ix_client_icon_id', table_name='client')
|
||||
op.drop_index('ix_batch_import_user_id', table_name='batch_import')
|
||||
op.drop_index('ix_batch_import_file_id', table_name='batch_import')
|
||||
op.drop_index('ix_auto_create_rule_custom_domain_id', table_name='auto_create_rule')
|
||||
op.drop_index('ix_authorized_address_user_id', table_name='authorized_address')
|
||||
op.drop_index('ix_authorization_code_user_id', table_name='authorization_code')
|
||||
op.drop_index('ix_authorization_code_client_id', table_name='authorization_code')
|
||||
op.drop_index('ix_api_key_user_id', table_name='api_key')
|
||||
op.drop_index('ix_api_key_code', table_name='api_key')
|
||||
op.drop_index('ix_api_to_cookie_token_user_id', table_name='api_cookie_token')
|
||||
op.drop_index('ix_api_to_cookie_token_api_key_id', table_name='api_cookie_token')
|
||||
op.drop_index('ix_alias_used_on_user_id', table_name='alias_used_on')
|
||||
op.drop_index('ix_alias_original_owner_id', table_name='alias')
|
||||
op.drop_index('ix_activation_code_user_id', table_name='activation_code')
|
@ -1,5 +1,5 @@
|
||||
from app.db import Session
|
||||
from app.models import Alias, Mailbox, AliasMailbox, User
|
||||
from app.models import Alias, Mailbox, AliasMailbox, User, CustomDomain
|
||||
from tests.utils import create_new_user, random_email
|
||||
|
||||
|
||||
@ -29,3 +29,23 @@ def test_alias_create_from_partner_flags_also_the_user():
|
||||
flush=True,
|
||||
)
|
||||
assert alias.user.flags & User.FLAG_CREATED_ALIAS_FROM_PARTNER > 0
|
||||
|
||||
|
||||
def test_alias_create_from_partner_domain_flags_the_alias():
|
||||
user = create_new_user()
|
||||
domain = CustomDomain.create(
|
||||
domain=random_email(),
|
||||
verified=True,
|
||||
user_id=user.id,
|
||||
partner_id=1,
|
||||
)
|
||||
Session.flush()
|
||||
email = random_email()
|
||||
alias = Alias.create(
|
||||
user_id=user.id,
|
||||
email=email,
|
||||
mailbox_id=user.default_mailbox_id,
|
||||
custom_domain_id=domain.id,
|
||||
flush=True,
|
||||
)
|
||||
assert alias.flags & Alias.FLAG_PARTNER_CREATED > 0
|
||||
|
@ -144,6 +144,21 @@ def test_login_case_from_web():
|
||||
assert audit_logs[0].action == UserAuditLogAction.LinkAccount.value
|
||||
|
||||
|
||||
def test_new_user_strategy_create_missing_link():
|
||||
email = random_email()
|
||||
user = User.create(email, commit=True)
|
||||
nus = NewUserStrategy(
|
||||
link_request=random_link_request(
|
||||
email=user.email, external_user_id=random_string(), from_partner=False
|
||||
),
|
||||
user=None,
|
||||
partner=get_proton_partner(),
|
||||
)
|
||||
result = nus.create_missing_link(user.email)
|
||||
assert result.user.id == user.id
|
||||
assert result.strategy == ExistingUnlinkedUserStrategy.__name__
|
||||
|
||||
|
||||
def test_get_strategy_existing_sl_user():
|
||||
email = random_email()
|
||||
user = User.create(email, commit=True)
|
||||
|
Reference in New Issue
Block a user