do not use flask-sqlalchemy

- add __tablename__ for all models
- use sa and orm instead of db
- rollback all changes in tests
- remove session in @app.teardown_appcontext
This commit is contained in:
Son 2021-10-12 14:36:47 +02:00
parent 653a03ac11
commit 372466ab06
98 changed files with 1338 additions and 1234 deletions

View File

@ -5,7 +5,7 @@ from flask_admin.actions import action
from flask_admin.contrib import sqla
from flask_login import current_user, login_user
from app.extensions import db
from app.db import Session
from app.models import User, ManualSubscription
@ -99,7 +99,7 @@ class UserAdmin(SLModelView):
"Extend trial for 1 week more?",
)
def extend_trial_1w(self, ids):
for user in User.query.filter(User.id.in_(ids)):
for user in User.filter(User.id.in_(ids)):
if user.trial_end and user.trial_end > arrow.now():
user.trial_end = user.trial_end.shift(weeks=1)
else:
@ -107,7 +107,7 @@ class UserAdmin(SLModelView):
flash(f"Extend trial for {user} to {user.trial_end}", "success")
db.session.commit()
Session.commit()
@action(
"disable_otp",
@ -115,12 +115,12 @@ class UserAdmin(SLModelView):
"Disable OTP?",
)
def disable_otp(self, ids):
for user in User.query.filter(User.id.in_(ids)):
for user in User.filter(User.id.in_(ids)):
if user.enable_otp:
user.enable_otp = False
flash(f"Disable OTP for {user}", "info")
db.session.commit()
Session.commit()
@action(
"login_as",
@ -132,16 +132,14 @@ class UserAdmin(SLModelView):
flash("only 1 user can be selected", "error")
return
for user in User.query.filter(User.id.in_(ids)):
for user in User.filter(User.id.in_(ids)):
login_user(user)
flash(f"Login as user {user}", "success")
return redirect("/")
def manual_upgrade(way: str, ids: [int], is_giveaway: bool):
query = User.query.filter(User.id.in_(ids))
for user in query.all():
for user in User.filter(User.id.in_(ids)).all():
manual_sub: ManualSubscription = ManualSubscription.get_by(user_id=user.id)
if manual_sub:
# renew existing subscription
@ -149,7 +147,7 @@ def manual_upgrade(way: str, ids: [int], is_giveaway: bool):
manual_sub.end_at = manual_sub.end_at.shift(years=1)
else:
manual_sub.end_at = arrow.now().shift(years=1, days=1)
db.session.commit()
Session.commit()
flash(f"Subscription extended to {manual_sub.end_at.humanize()}", "success")
continue
@ -211,11 +209,11 @@ class ManualSubscriptionAdmin(SLModelView):
"Extend 1 year more?",
)
def extend_1y(self, ids):
for ms in ManualSubscription.query.filter(ManualSubscription.id.in_(ids)):
for ms in ManualSubscription.filter(ManualSubscription.id.in_(ids)):
ms.end_at = ms.end_at.shift(years=1)
flash(f"Extend subscription for {ms.user}", "success")
db.session.commit()
Session.commit()
class ClientAdmin(SLModelView):

View File

@ -1,10 +1,11 @@
import re2 as re
from typing import Optional
import re2 as re
from email_validator import validate_email, EmailNotValidError
from sqlalchemy.exc import IntegrityError, DataError
from app.config import BOUNCE_PREFIX_FOR_REPLY_PHASE
from app.db import Session
from app.email_utils import (
get_email_domain_part,
send_cannot_create_directory_alias,
@ -14,7 +15,6 @@ from app.email_utils import (
get_email_local_part,
)
from app.errors import AliasInTrashError
from app.extensions import db
from app.log import LOG
from app.models import (
Alias,
@ -97,14 +97,14 @@ def try_auto_create_directory(address: str) -> Optional[Alias]:
mailbox_id=mailboxes[0].id,
note=f"Created by directory {directory.name}",
)
db.session.flush()
Session.flush()
for i in range(1, len(mailboxes)):
AliasMailbox.create(
alias_id=alias.id,
mailbox_id=mailboxes[i].id,
)
db.session.commit()
Session.commit()
return alias
except AliasInTrashError:
LOG.w(
@ -116,7 +116,7 @@ def try_auto_create_directory(address: str) -> Optional[Alias]:
return None
except IntegrityError:
LOG.w("Alias %s already exists", address)
db.session.rollback()
Session.rollback()
alias = Alias.get_by(email=address)
return alias
@ -173,13 +173,13 @@ def try_auto_create_via_domain(address: str) -> Optional[Alias]:
mailbox_id=mailboxes[0].id,
note=alias_note,
)
db.session.flush()
Session.flush()
for i in range(1, len(mailboxes)):
AliasMailbox.create(
alias_id=alias.id,
mailbox_id=mailboxes[i].id,
)
db.session.commit()
Session.commit()
return alias
except AliasInTrashError:
LOG.w(
@ -191,12 +191,12 @@ def try_auto_create_via_domain(address: str) -> Optional[Alias]:
return None
except IntegrityError:
LOG.w("Alias %s already exists", address)
db.session.rollback()
Session.rollback()
alias = Alias.get_by(email=address)
return alias
except DataError:
LOG.w("Cannot create alias %s", address)
db.session.rollback()
Session.rollback()
return None
@ -211,30 +211,30 @@ def delete_alias(alias: Alias, user: User):
email=alias.email, domain_id=alias.custom_domain_id
):
LOG.d("add %s to domain %s trash", alias, alias.custom_domain_id)
db.session.add(
Session.add(
DomainDeletedAlias(
user_id=user.id, email=alias.email, domain_id=alias.custom_domain_id
)
)
db.session.commit()
Session.commit()
else:
if not DeletedAlias.get_by(email=alias.email):
LOG.d("add %s to global trash", alias)
db.session.add(DeletedAlias(email=alias.email))
db.session.commit()
Session.add(DeletedAlias(email=alias.email))
Session.commit()
Alias.query.filter(Alias.id == alias.id).delete()
db.session.commit()
Alias.filter(Alias.id == alias.id).delete()
Session.commit()
def aliases_for_mailbox(mailbox: Mailbox) -> [Alias]:
"""
get list of aliases for a given mailbox
"""
ret = set(Alias.query.filter(Alias.mailbox_id == mailbox.id).all())
ret = set(Alias.filter(Alias.mailbox_id == mailbox.id).all())
for alias in (
db.session.query(Alias)
Session.query(Alias)
.join(AliasMailbox, Alias.id == AliasMailbox.alias_id)
.filter(AliasMailbox.mailbox_id == mailbox.id)
):
@ -247,7 +247,7 @@ def nb_email_log_for_mailbox(mailbox: Mailbox):
aliases = aliases_for_mailbox(mailbox)
alias_ids = [alias.id for alias in aliases]
return (
db.session.query(EmailLog)
Session.query(EmailLog)
.join(Contact, EmailLog.contact_id == Contact.id)
.filter(Contact.alias_id.in_(alias_ids))
.count()

View File

@ -4,7 +4,7 @@ import arrow
from flask import Blueprint, request, jsonify, g
from flask_login import current_user
from app.extensions import db
from app.db import Session
from app.models import ApiKey
api_bp = Blueprint(name="api", import_name=__name__, url_prefix="/api")
@ -26,7 +26,7 @@ def require_api_auth(f):
# Update api key stats
api_key.last_used = arrow.now()
api_key.times += 1
db.session.commit()
Session.commit()
g.user = api_key.user

View File

@ -6,7 +6,7 @@ from sqlalchemy import or_, func, case, and_
from sqlalchemy.orm import joinedload
from app.config import PAGE_LIMIT
from app.extensions import db
from app.db import Session
from app.models import (
Alias,
Contact,
@ -117,7 +117,7 @@ def serialize_contact(contact: Contact, existed=False) -> dict:
def get_alias_infos_with_pagination(user, page_id=0, query=None) -> [AliasInfo]:
ret = []
q = (
db.session.query(Alias)
Session.query(Alias)
.options(joinedload(Alias.mailbox))
.filter(Alias.user_id == user.id)
.order_by(Alias.created_at.desc())
@ -221,7 +221,7 @@ def get_alias_infos_with_pagination_v3(
def get_alias_info(alias: Alias) -> AliasInfo:
q = (
db.session.query(Contact, EmailLog)
Session.query(Contact, EmailLog)
.filter(Contact.alias_id == alias.id)
.filter(EmailLog.contact_id == Contact.id)
)
@ -251,7 +251,7 @@ def get_alias_info_v2(alias: Alias, mailbox=None) -> AliasInfo:
mailbox = alias.mailbox
q = (
db.session.query(Contact, EmailLog)
Session.query(Contact, EmailLog)
.filter(Contact.alias_id == alias.id)
.filter(EmailLog.contact_id == Contact.id)
)
@ -297,7 +297,7 @@ def get_alias_info_v2(alias: Alias, mailbox=None) -> AliasInfo:
def get_alias_contacts(alias, page_id: int) -> [dict]:
q = (
Contact.query.filter_by(alias_id=alias.id)
Contact.filter_by(alias_id=alias.id)
.order_by(Contact.id.desc())
.limit(PAGE_LIMIT)
.offset(page_id * PAGE_LIMIT)
@ -332,7 +332,7 @@ def get_alias_info_v3(user: User, alias_id: int) -> AliasInfo:
def construct_alias_query(user: User):
# subquery on alias annotated with nb_reply, nb_blocked, nb_forward, max_created_at, latest_email_log_created_at
alias_activity_subquery = (
db.session.query(
Session.query(
Alias.id,
func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"),
func.sum(
@ -364,7 +364,7 @@ def construct_alias_query(user: User):
)
alias_contact_subquery = (
db.session.query(Alias.id, func.max(Contact.id).label("max_contact_id"))
Session.query(Alias.id, func.max(Contact.id).label("max_contact_id"))
.join(Contact, Alias.id == Contact.alias_id, isouter=True)
.filter(Alias.user_id == user.id)
.group_by(Alias.id)
@ -372,7 +372,7 @@ def construct_alias_query(user: User):
)
return (
db.session.query(
Session.query(
Alias,
Contact,
EmailLog,

View File

@ -17,10 +17,10 @@ from app.api.serializer import (
get_alias_infos_with_pagination_v3,
)
from app.dashboard.views.alias_log import get_alias_log
from app.db import Session
from app.email_utils import (
generate_reply_email,
)
from app.extensions import db
from app.log import LOG
from app.models import Alias, Contact, Mailbox, AliasMailbox
from app.utils import sanitize_email
@ -164,7 +164,7 @@ def toggle_alias(alias_id):
return jsonify(error="Forbidden"), 403
alias.enabled = not alias.enabled
db.session.commit()
Session.commit()
return jsonify(enabled=alias.enabled), 200
@ -280,8 +280,8 @@ def update_alias(alias_id):
# <<< update alias mailboxes >>>
# first remove all existing alias-mailboxes links
AliasMailbox.query.filter_by(alias_id=alias.id).delete()
db.session.flush()
AliasMailbox.filter_by(alias_id=alias.id).delete()
Session.flush()
# then add all new mailboxes
for i, mailbox in enumerate(mailboxes):
@ -310,7 +310,7 @@ def update_alias(alias_id):
changed = True
if changed:
db.session.commit()
Session.commit()
return jsonify(ok=True), 200
@ -422,7 +422,7 @@ def create_contact_route(alias_id):
)
LOG.d("create reverse-alias for %s %s", contact_addr, alias)
db.session.commit()
Session.commit()
return jsonify(**serialize_contact(contact)), 201
@ -444,6 +444,6 @@ def delete_contact(contact_id):
return jsonify(error="Forbidden"), 403
Contact.delete(contact_id)
db.session.commit()
Session.commit()
return jsonify(deleted=True), 200

View File

@ -5,7 +5,7 @@ from app.api.base import api_bp, require_api_auth
from app.dashboard.views.custom_alias import (
get_available_suffixes,
)
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import AliasUsedOn, Alias, User
from app.utils import convert_to_id
@ -43,7 +43,7 @@ def options_v4():
if hostname:
# put the latest used alias first
q = (
db.session.query(AliasUsedOn, Alias, User)
Session.query(AliasUsedOn, Alias, User)
.filter(
AliasUsedOn.alias_id == Alias.id,
Alias.user_id == user.id,
@ -114,7 +114,7 @@ def options_v5():
if hostname:
# put the latest used alias first
q = (
db.session.query(AliasUsedOn, Alias, User)
Session.query(AliasUsedOn, Alias, User)
.filter(
AliasUsedOn.alias_id == Alias.id,
Alias.user_id == user.id,

View File

@ -9,7 +9,7 @@ from requests import RequestException
from app.api.base import api_bp, require_api_auth
from app.config import APPLE_API_SECRET, MACAPP_APPLE_API_SECRET
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import PlanEnum, AppleSubscription
@ -279,7 +279,7 @@ def apple_update_notification():
apple_sub.receipt_data = data["unified_receipt"]["latest_receipt"]
apple_sub.expires_date = expires_date
apple_sub.plan = plan
db.session.commit()
Session.commit()
return jsonify(ok=True), 200
else:
LOG.w(
@ -544,6 +544,6 @@ def verify_receipt(receipt_data, user, password) -> Optional[AppleSubscription]:
plan=plan,
)
db.session.commit()
Session.commit()
return apple_sub

View File

@ -11,13 +11,14 @@ from app import email_utils
from app.api.base import api_bp
from app.config import FLASK_SECRET, DISABLE_REGISTRATION
from app.dashboard.views.setting import send_reset_password_email
from app.db import Session
from app.email_utils import (
email_can_be_used_as_mailbox,
personal_email_already_used,
send_email,
render,
)
from app.extensions import db, limiter
from app.extensions import limiter
from app.log import LOG
from app.models import User, ApiKey, SocialAuth, AccountActivation
from app.utils import sanitize_email
@ -98,12 +99,12 @@ def auth_register():
LOG.d("create user %s", email)
user = User.create(email=email, name="", password=password)
db.session.flush()
Session.flush()
# create activation code
code = "".join([str(random.randint(0, 9)) for _ in range(6)])
AccountActivation.create(user_id=user.id, code=code)
db.session.commit()
Session.commit()
send_email(
email,
@ -155,13 +156,13 @@ def auth_activate():
if account_activation.code != code:
# decrement nb tries
account_activation.tries -= 1
db.session.commit()
Session.commit()
# Trigger rate limiter
g.deduct_limit = True
if account_activation.tries == 0:
AccountActivation.delete(account_activation.id)
db.session.commit()
Session.commit()
return jsonify(error="Too many wrong tries"), 410
return jsonify(error="Wrong email or code"), 400
@ -169,7 +170,7 @@ def auth_activate():
LOG.d("activate user %s", user)
user.activated = True
AccountActivation.delete(account_activation.id)
db.session.commit()
Session.commit()
return jsonify(msg="Account is activated, user can login now"), 200
@ -198,12 +199,12 @@ def auth_reactivate():
account_activation = AccountActivation.get_by(user_id=user.id)
if account_activation:
AccountActivation.delete(account_activation.id)
db.session.commit()
Session.commit()
# create activation code
code = "".join([str(random.randint(0, 9)) for _ in range(6)])
AccountActivation.create(user_id=user.id, code=code)
db.session.commit()
Session.commit()
send_email(
email,
@ -255,12 +256,12 @@ def auth_facebook():
LOG.d("create facebook user with %s", user_info)
user = User.create(email=email, name=user_info["name"], activated=True)
db.session.commit()
Session.commit()
email_utils.send_welcome_email(user)
if not SocialAuth.get_by(user_id=user.id, social="facebook"):
SocialAuth.create(user_id=user.id, social="facebook")
db.session.commit()
Session.commit()
return jsonify(**auth_payload(user, device)), 200
@ -308,12 +309,12 @@ def auth_google():
LOG.d("create Google user with %s", user_info)
user = User.create(email=email, name="", activated=True)
db.session.commit()
Session.commit()
email_utils.send_welcome_email(user)
if not SocialAuth.get_by(user_id=user.id, social="google"):
SocialAuth.create(user_id=user.id, social="google")
db.session.commit()
Session.commit()
return jsonify(**auth_payload(user, device)), 200
@ -331,7 +332,7 @@ def auth_payload(user, device) -> dict:
if not api_key:
LOG.d("create new api key for %s and %s", user, device)
api_key = ApiKey.create(user.id, device)
db.session.commit()
Session.commit()
ret["mfa_key"] = None
ret["api_key"] = api_key.code

View File

@ -5,7 +5,7 @@ from itsdangerous import Signer
from app.api.base import api_bp
from app.config import FLASK_SECRET
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import User, ApiKey
@ -61,7 +61,7 @@ def auth_mfa():
if not api_key:
LOG.d("create new api key for %s and %s", user, device)
api_key = ApiKey.create(user.id, device)
db.session.commit()
Session.commit()
ret["api_key"] = api_key.code

View File

@ -2,7 +2,7 @@ from flask import g, request
from flask import jsonify
from app.api.base import api_bp, require_api_auth
from app.extensions import db
from app.db import Session
from app.models import CustomDomain, DomainDeletedAlias, Mailbox, DomainMailbox
@ -108,8 +108,8 @@ def update_custom_domain(custom_domain_id):
mailboxes.append(mailbox)
# first remove all existing domain-mailboxes links
DomainMailbox.query.filter_by(domain_id=custom_domain.id).delete()
db.session.flush()
DomainMailbox.filter_by(domain_id=custom_domain.id).delete()
Session.flush()
for mailbox in mailboxes:
DomainMailbox.create(domain_id=custom_domain.id, mailbox_id=mailbox.id)
@ -117,6 +117,6 @@ def update_custom_domain(custom_domain_id):
changed = True
if changed:
db.session.commit()
Session.commit()
return jsonify(ok=True), 200

View File

@ -7,12 +7,12 @@ from flask import request
from app.api.base import api_bp, require_api_auth
from app.dashboard.views.mailbox import send_verification_email
from app.dashboard.views.mailbox_detail import verify_mailbox_change
from app.db import Session
from app.email_utils import (
mailbox_already_used,
email_can_be_used_as_mailbox,
is_valid_email,
)
from app.extensions import db
from app.models import Mailbox
from app.utils import sanitize_email
@ -58,7 +58,7 @@ def create_mailbox():
)
else:
new_mailbox = Mailbox.create(email=mailbox_email, user_id=user.id)
db.session.commit()
Session.commit()
send_verification_email(user, new_mailbox)
@ -89,7 +89,7 @@ def delete_mailbox(mailbox_id):
return jsonify(error="You cannot delete the default mailbox"), 400
Mailbox.delete(mailbox_id)
db.session.commit()
Session.commit()
return jsonify(deleted=True), 200
@ -158,7 +158,7 @@ def update_mailbox(mailbox_id):
changed = True
if changed:
db.session.commit()
Session.commit()
return jsonify(updated=True), 200
@ -190,7 +190,7 @@ def get_mailboxes_v2():
user = g.user
mailboxes = []
for mailbox in Mailbox.query.filter_by(user_id=user.id):
for mailbox in Mailbox.filter_by(user_id=user.id):
mailboxes.append(mailbox)
return (

View File

@ -10,7 +10,8 @@ from app.api.serializer import (
)
from app.config import MAX_NB_EMAIL_FREE_PLAN, ALIAS_LIMIT
from app.dashboard.views.custom_alias import verify_prefix_suffix, signer
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import (
Alias,
@ -108,11 +109,11 @@ def new_custom_alias_v2():
custom_domain_id=custom_domain_id,
)
db.session.commit()
Session.commit()
if hostname:
AliasUsedOn.create(alias_id=alias.id, hostname=hostname, user_id=alias.user_id)
db.session.commit()
Session.commit()
return (
jsonify(alias=full_alias, **serialize_alias_info_v2(get_alias_info_v2(alias))),
@ -217,7 +218,7 @@ def new_custom_alias_v3():
mailbox_id=mailboxes[0].id,
custom_domain_id=custom_domain_id,
)
db.session.flush()
Session.flush()
for i in range(1, len(mailboxes)):
AliasMailbox.create(
@ -225,11 +226,11 @@ def new_custom_alias_v3():
mailbox_id=mailboxes[i].id,
)
db.session.commit()
Session.commit()
if hostname:
AliasUsedOn.create(alias_id=alias.id, hostname=hostname, user_id=alias.user_id)
db.session.commit()
Session.commit()
return (
jsonify(alias=full_alias, **serialize_alias_info_v2(get_alias_info_v2(alias))),

View File

@ -7,7 +7,8 @@ from app.api.serializer import (
serialize_alias_info_v2,
)
from app.config import MAX_NB_EMAIL_FREE_PLAN, ALIAS_LIMIT
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import Alias, AliasUsedOn, AliasGeneratorEnum
@ -51,12 +52,12 @@ def new_random_alias():
return jsonify(error=f"{mode} must be either word or uuid"), 400
alias = Alias.create_new_random(user=user, scheme=scheme, note=note)
db.session.commit()
Session.commit()
hostname = request.args.get("hostname")
if hostname:
AliasUsedOn.create(alias_id=alias.id, hostname=hostname, user_id=alias.user_id)
db.session.commit()
Session.commit()
return (
jsonify(alias=alias.email, **serialize_alias_info_v2(get_alias_info_v2(alias))),

View File

@ -4,7 +4,7 @@ from flask import request
from app.api.base import api_bp, require_api_auth
from app.config import PAGE_LIMIT
from app.extensions import db
from app.db import Session
from app.models import Notification
@ -32,7 +32,7 @@ def get_notifications():
return jsonify(error="page must be provided in request query"), 400
notifications = (
Notification.query.filter_by(user_id=user.id)
Notification.filter_by(user_id=user.id)
.order_by(Notification.read, Notification.created_at.desc())
.limit(PAGE_LIMIT + 1) # load a record more to know whether there's more
.offset(page * PAGE_LIMIT)
@ -76,6 +76,6 @@ def mark_as_read(notification_id):
return jsonify(error="Forbidden"), 403
notification.read = True
db.session.commit()
Session.commit()
return jsonify(done=True), 200

View File

@ -2,7 +2,7 @@ import arrow
from flask import jsonify, g, request
from app.api.base import api_bp, require_api_auth
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import (
User,
@ -93,7 +93,7 @@ def update_setting():
user.default_alias_custom_domain_id = custom_domain.id
user.default_alias_public_domain_id = None
db.session.commit()
Session.commit()
return jsonify(setting_to_dict(user))

View File

@ -7,7 +7,7 @@ from flask_login import logout_user
from app import s3
from app.api.base import api_bp, require_api_auth
from app.config import SESSION_COOKIE_NAME
from app.extensions import db
from app.db import Session
from app.models import ApiKey, File, User
from app.utils import random_string
@ -56,24 +56,24 @@ def update_user_info():
if user.profile_picture_id:
file = user.profile_picture
user.profile_picture_id = None
db.session.flush()
Session.flush()
if file:
File.delete(file.id)
s3.delete(file.path)
db.session.flush()
Session.flush()
else:
raw_data = base64.decodebytes(data["profile_picture"].encode())
file_path = random_string(30)
file = File.create(user_id=user.id, path=file_path)
db.session.flush()
Session.flush()
s3.upload_from_bytesio(file_path, BytesIO(raw_data))
user.profile_picture_id = file.id
db.session.flush()
Session.flush()
if "name" in data:
user.name = data["name"]
db.session.commit()
Session.commit()
return jsonify(user_to_dict(user))
@ -95,7 +95,7 @@ def create_api_key():
device = data.get("device")
api_key = ApiKey.create(user_id=g.user.id, name=device)
db.session.commit()
Session.commit()
return jsonify(api_key=api_key.code), 201

View File

@ -3,7 +3,8 @@ from flask_login import login_user, current_user
from app import email_utils
from app.auth.base import auth_bp
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import ActivationCode
@ -50,7 +51,7 @@ def activate():
# activation code is to be used only once
ActivationCode.delete(activation_code.id)
db.session.commit()
Session.commit()
flash("Your account has been activated", "success")

View File

@ -2,7 +2,7 @@ from flask import request, flash, render_template, redirect, url_for
from flask_login import login_user
from app.auth.base import auth_bp
from app.extensions import db
from app.db import Session
from app.models import EmailChange
@ -18,14 +18,14 @@ def change_email():
if email_change.is_expired():
# delete the expired email
EmailChange.delete(email_change.id)
db.session.commit()
Session.commit()
return render_template("auth/change_email.html")
user = email_change.user
user.email = email_change.new_email
EmailChange.delete(email_change.id)
db.session.commit()
Session.commit()
flash("Your new email has been updated", "success")

View File

@ -9,7 +9,7 @@ from app.config import (
FACEBOOK_CLIENT_ID,
FACEBOOK_CLIENT_SECRET,
)
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import User, SocialAuth
from .login_utils import after_login
@ -102,7 +102,7 @@ def facebook_callback():
LOG.d("set user profile picture to %s", picture_url)
file = create_file_from_url(user, picture_url)
user.profile_picture_id = file.id
db.session.commit()
Session.commit()
else:
flash(
@ -122,6 +122,6 @@ def facebook_callback():
if not SocialAuth.get_by(user_id=user.id, social="facebook"):
SocialAuth.create(user_id=user.id, social="facebook")
db.session.commit()
Session.commit()
return after_login(user, next_url)

View File

@ -19,7 +19,8 @@ from wtforms import HiddenField, validators, BooleanField
from app.auth.base import auth_bp
from app.config import MFA_USER_ID
from app.config import RP_ID, URL
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import User, Fido, MfaBrowser
@ -102,7 +103,7 @@ def fido():
auto_activate = False
else:
user.fido_sign_count = new_sign_count
db.session.commit()
Session.commit()
del session[MFA_USER_ID]
login_user(user)
@ -113,7 +114,7 @@ def fido():
if fido_token_form.remember.data:
browser = MfaBrowser.create_new(user=user)
db.session.commit()
Session.commit()
response.set_cookie(
"mfa",
value=browser.token,

View File

@ -4,7 +4,7 @@ from requests_oauthlib import OAuth2Session
from app.auth.base import auth_bp
from app.auth.views.login_utils import after_login
from app.config import GITHUB_CLIENT_ID, GITHUB_CLIENT_SECRET, URL
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import User, SocialAuth
from app.utils import encode_url, sanitize_email
@ -94,7 +94,7 @@ def github_callback():
if not SocialAuth.get_by(user_id=user.id, social="github"):
SocialAuth.create(user_id=user.id, social="github")
db.session.commit()
Session.commit()
# The activation link contains the original page, for ex authorize page
next_url = request.args.get("next") if request.args else None

View File

@ -4,7 +4,7 @@ from requests_oauthlib import OAuth2Session
from app import s3
from app.auth.base import auth_bp
from app.config import URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import User, File, SocialAuth
from app.utils import random_string, sanitize_email
@ -89,7 +89,7 @@ def google_callback():
LOG.d("set user profile picture to %s", picture_url)
file = create_file_from_url(user, picture_url)
user.profile_picture_id = file.id
db.session.commit()
Session.commit()
else:
flash(
"Sorry you cannot sign up via Google, please use email/password sign-up instead",
@ -108,7 +108,7 @@ def google_callback():
if not SocialAuth.get_by(user_id=user.id, social="google"):
SocialAuth.create(user_id=user.id, social="google")
db.session.commit()
Session.commit()
return after_login(user, next_url)
@ -119,7 +119,7 @@ def create_file_from_url(user, url) -> File:
s3.upload_from_url(url, file_path)
db.session.flush()
Session.flush()
LOG.d("upload file %s to s3", file)
return file

View File

@ -15,7 +15,8 @@ from wtforms import BooleanField, StringField, validators
from app.auth.base import auth_bp
from app.config import MFA_USER_ID, URL
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.models import User, MfaBrowser
@ -67,7 +68,7 @@ def mfa():
if totp.verify(token) and user.last_otp != token:
del session[MFA_USER_ID]
user.last_otp = token
db.session.commit()
Session.commit()
login_user(user)
flash(f"Welcome back!", "success")
@ -77,7 +78,7 @@ def mfa():
if otp_token_form.remember.data:
browser = MfaBrowser.create_new(user=user)
db.session.commit()
Session.commit()
response.set_cookie(
"mfa",
value=browser.token,

View File

@ -6,7 +6,8 @@ from wtforms import StringField, validators
from app.auth.base import auth_bp
from app.config import MFA_USER_ID
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import User, RecoveryCode
@ -54,7 +55,7 @@ def recovery_route():
recovery_code.used = True
recovery_code.used_at = arrow.now()
db.session.commit()
Session.commit()
# User comes to login page from another page
if next_url:

View File

@ -8,11 +8,11 @@ from app import email_utils, config
from app.auth.base import auth_bp
from app.auth.views.login_utils import get_referral
from app.config import URL, HCAPTCHA_SECRET, HCAPTCHA_SITEKEY
from app.db import Session
from app.email_utils import (
email_can_be_used_as_mailbox,
personal_email_already_used,
)
from app.extensions import db
from app.log import LOG
from app.models import User, ActivationCode
from app.utils import random_string, encode_url, sanitize_email
@ -81,7 +81,7 @@ def register():
password=form.password.data,
referral=get_referral(),
)
db.session.commit()
Session.commit()
try:
send_activation_email(user, next_url)
@ -102,7 +102,7 @@ def register():
def send_activation_email(user, next_url):
# the activation code is valid for 1h
activation = ActivationCode.create(user_id=user.id, code=random_string(30))
db.session.commit()
Session.commit()
# Send user activation email
activation_link = f"{URL}/auth/activate?code={activation.code}"

View File

@ -6,7 +6,8 @@ from wtforms import StringField, validators
from app.auth.base import auth_bp
from app.auth.views.login_utils import after_login
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.models import ResetPasswordCode
@ -64,7 +65,7 @@ def reset_password():
# change the alternative_id to log user out on other browsers
user.alternative_id = str(uuid.uuid4())
db.session.commit()
Session.commit()
# do not use login_user(user) here
# to make sure user needs to go through MFA if enabled

View File

@ -10,12 +10,12 @@ from wtforms import StringField, validators, ValidationError
from app.config import PAGE_LIMIT
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import (
is_valid_email,
generate_reply_email,
parse_full_address,
)
from app.extensions import db
from app.log import LOG
from app.models import Alias, Contact, EmailLog
@ -64,7 +64,7 @@ def get_contact_infos(
) -> [ContactInfo]:
"""if contact_id is set, only return the contact info for this contact"""
sub = (
db.session.query(
Session.query(
Contact.id,
func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"),
func.sum(
@ -94,7 +94,7 @@ def get_contact_infos(
)
q = (
db.session.query(
Session.query(
Contact,
EmailLog,
sub.c.nb_reply,
@ -221,7 +221,7 @@ def alias_contact_manager(alias_id):
)
LOG.d("create reverse-alias for %s", contact_addr)
db.session.commit()
Session.commit()
flash(f"Reverse alias for {contact_addr} is created", "success")
return redirect(
@ -248,7 +248,7 @@ def alias_contact_manager(alias_id):
delete_contact_email = contact.website_email
Contact.delete(contact_id)
db.session.commit()
Session.commit()
flash(
f"Reverse-alias for {delete_contact_email} has been deleted", "success"

View File

@ -4,7 +4,7 @@ from flask_login import login_required, current_user
from app.config import PAGE_LIMIT
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.models import Alias, EmailLog, Contact
@ -43,7 +43,7 @@ def alias_log(alias_id, page_id):
logs = get_alias_log(alias, page_id)
base = (
db.session.query(Contact, EmailLog)
Session.query(Contact, EmailLog)
.filter(Contact.id == EmailLog.contact_id)
.filter(Contact.alias_id == alias.id)
)
@ -66,7 +66,7 @@ def get_alias_log(alias: Alias, page_id=0) -> [AliasLog]:
logs: [AliasLog] = []
q = (
db.session.query(Contact, EmailLog)
Session.query(Contact, EmailLog)
.filter(Contact.id == EmailLog.contact_id)
.filter(Contact.alias_id == alias.id)
.order_by(EmailLog.id.desc())

View File

@ -5,8 +5,9 @@ from flask_login import login_required, current_user
from app.config import URL
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import send_email, render
from app.extensions import db, limiter
from app.extensions import limiter
from app.log import LOG
from app.models import (
Alias,
@ -25,20 +26,20 @@ def transfer(alias, new_user, new_mailboxes: [Mailbox]):
raise Exception("Cannot transfer alias that's used to receive newsletter")
# update user_id
db.session.query(Contact).filter(Contact.alias_id == alias.id).update(
Session.query(Contact).filter(Contact.alias_id == alias.id).update(
{"user_id": new_user.id}
)
db.session.query(AliasUsedOn).filter(AliasUsedOn.alias_id == alias.id).update(
Session.query(AliasUsedOn).filter(AliasUsedOn.alias_id == alias.id).update(
{"user_id": new_user.id}
)
db.session.query(ClientUser).filter(ClientUser.alias_id == alias.id).update(
Session.query(ClientUser).filter(ClientUser.alias_id == alias.id).update(
{"user_id": new_user.id}
)
# remove existing mailboxes from the alias
db.session.query(AliasMailbox).filter(AliasMailbox.alias_id == alias.id).delete()
Session.query(AliasMailbox).filter(AliasMailbox.alias_id == alias.id).delete()
# set mailboxes
alias.mailbox_id = new_mailboxes.pop().id
@ -71,7 +72,7 @@ def transfer(alias, new_user, new_mailboxes: [Mailbox]):
alias.disable_pgp = False
alias.pinned = False
db.session.commit()
Session.commit()
@dashboard_bp.route("/alias_transfer/send/<int:alias_id>/", methods=["GET", "POST"])
@ -100,7 +101,7 @@ def alias_transfer_send_route(alias_id):
if request.method == "POST":
if request.form.get("form-name") == "create":
alias.transfer_token = str(uuid4())
db.session.commit()
Session.commit()
alias_transfer_url = (
URL
+ "/dashboard/alias_transfer/receive"
@ -111,7 +112,7 @@ def alias_transfer_send_route(alias_id):
# request.form.get("form-name") == "remove"
else:
alias.transfer_token = None
db.session.commit()
Session.commit()
alias_transfer_url = None
flash("Share URL deleted", "success")
return redirect(request.url)

View File

@ -4,7 +4,7 @@ from flask_wtf import FlaskForm
from wtforms import StringField, validators
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.models import ApiKey
@ -16,7 +16,7 @@ class NewApiKeyForm(FlaskForm):
@login_required
def api_key():
api_keys = (
ApiKey.query.filter(ApiKey.user_id == current_user.id)
ApiKey.filter(ApiKey.user_id == current_user.id)
.order_by(ApiKey.created_at.desc())
.all()
)
@ -38,7 +38,7 @@ def api_key():
name = api_key.name
ApiKey.delete(api_key_id)
db.session.commit()
Session.commit()
flash(f"API Key {name} has been deleted", "success")
return redirect(url_for("dashboard.api_key"))
@ -48,7 +48,7 @@ def api_key():
new_api_key = ApiKey.create(
name=new_api_key_form.name.data, user_id=current_user.id
)
db.session.commit()
Session.commit()
flash(f"New API Key {new_api_key.name} has been created", "success")
return redirect(url_for("dashboard.api_key"))

View File

@ -1,3 +1,5 @@
from app.db import Session
"""
List of apps that user has used via the "Sign in with SimpleLogin"
"""
@ -7,7 +9,6 @@ from flask_login import login_required, current_user
from sqlalchemy.orm import joinedload
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.models import (
ClientUser,
)
@ -36,7 +37,7 @@ def app_route():
client = client_user.client
ClientUser.delete(client_user_id)
db.session.commit()
Session.commit()
flash(f"Link with {client.name} has been removed", "success")
return redirect(request.url)

View File

@ -5,7 +5,7 @@ from flask_login import login_required, current_user
from app import s3
from app.config import JOB_BATCH_IMPORT
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import File, BatchImport, Job
from app.utils import random_string
@ -18,7 +18,7 @@ def batch_import_route():
if not current_user.verified_custom_domains():
flash("Alias batch import is only available for custom domains", "warning")
batch_imports = BatchImport.query.filter_by(user_id=current_user.id).all()
batch_imports = BatchImport.filter_by(user_id=current_user.id).all()
if request.method == "POST":
alias_file = request.files["alias-file"]
@ -26,11 +26,11 @@ def batch_import_route():
file_path = random_string(20) + ".csv"
file = File.create(user_id=current_user.id, path=file_path)
s3.upload_from_bytesio(file_path, alias_file)
db.session.flush()
Session.flush()
LOG.d("upload file %s to s3 at %s", file, file_path)
bi = BatchImport.create(user_id=current_user.id, file_id=file.id)
db.session.flush()
Session.flush()
LOG.d("Add a batch import job %s for %s", bi, current_user)
# Schedule batch import job
@ -39,7 +39,7 @@ def batch_import_route():
payload={"batch_import_id": bi.id},
run_at=arrow.now(),
)
db.session.commit()
Session.commit()
flash(
"The file has been uploaded successfully and the import will start shortly",

View File

@ -3,7 +3,7 @@ from flask_login import login_required, current_user
from app.config import PADDLE_MONTHLY_PRODUCT_ID, PADDLE_YEARLY_PRODUCT_ID
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import Subscription, PlanEnum
from app.paddle_utils import cancel_subscription, change_plan
@ -26,7 +26,7 @@ def billing():
if success:
sub.cancelled = True
db.session.commit()
Session.commit()
flash("Your subscription has been canceled successfully", "success")
else:
flash(
@ -44,7 +44,7 @@ def billing():
if success:
sub.plan = PlanEnum.monthly
db.session.commit()
Session.commit()
flash("Your subscription has been updated", "success")
else:
if msg:
@ -65,7 +65,7 @@ def billing():
if success:
sub.plan = PlanEnum.yearly
db.session.commit()
Session.commit()
flash("Your subscription has been updated", "success")
else:
if msg:

View File

@ -2,7 +2,7 @@ from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.models import Contact
from app.pgp_utils import PGPException, load_public_key_and_check
@ -34,7 +34,7 @@ def contact_detail_route(contact_id):
except PGPException:
flash("Cannot add the public key, please verify it", "error")
else:
db.session.commit()
Session.commit()
flash(
f"PGP public key for {contact.email} is saved successfully",
"success",
@ -46,7 +46,7 @@ def contact_detail_route(contact_id):
# Free user can decide to remove contact PGP key
contact.pgp_public_key = None
contact.pgp_finger_print = None
db.session.commit()
Session.commit()
flash(f"PGP public key for {contact.email} is removed", "success")
return redirect(
url_for("dashboard.contact_detail_route", contact_id=contact_id)

View File

@ -6,8 +6,8 @@ from wtforms import StringField, validators
from app.config import ADMIN_EMAIL
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import send_email
from app.extensions import db
from app.models import (
ManualSubscription,
Coupon,
@ -57,7 +57,7 @@ def coupon_route():
if coupon and not coupon.used:
coupon.used_by_user_id = current_user.id
coupon.used = True
db.session.commit()
Session.commit()
manual_sub: ManualSubscription = ManualSubscription.get_by(
user_id=current_user.id
@ -68,7 +68,7 @@ def coupon_route():
manual_sub.end_at = manual_sub.end_at.shift(years=coupon.nb_year)
else:
manual_sub.end_at = arrow.now().shift(years=coupon.nb_year, days=1)
db.session.commit()
Session.commit()
flash(
f"Your current subscription is extended to {manual_sub.end_at.humanize()}",
"success",

View File

@ -13,7 +13,8 @@ from app.config import (
ALIAS_LIMIT,
)
from app.dashboard.base import dashboard_bp
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import (
Alias,
@ -307,10 +308,10 @@ def custom_alias():
mailbox_id=mailboxes[0].id,
custom_domain_id=custom_domain_id,
)
db.session.flush()
Session.flush()
except IntegrityError:
LOG.w("Alias %s already exists", full_alias)
db.session.rollback()
Session.rollback()
flash("Unknown error, please retry", "error")
return redirect(url_for("dashboard.custom_alias"))
@ -320,7 +321,7 @@ def custom_alias():
mailbox_id=mailboxes[i].id,
)
db.session.commit()
Session.commit()
flash(f"Alias {full_alias} has been created", "success")
return redirect(url_for("dashboard.index", highlight_alias_id=alias.id))

View File

@ -5,8 +5,8 @@ from wtforms import StringField, validators
from app.config import EMAIL_SERVERS_WITH_PRIORITY
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import get_email_domain_part
from app.extensions import db
from app.models import CustomDomain, Mailbox, DomainMailbox, SLDomain
@ -19,7 +19,7 @@ class NewCustomDomainForm(FlaskForm):
@dashboard_bp.route("/custom_domain", methods=["GET", "POST"])
@login_required
def custom_domain():
custom_domains = CustomDomain.query.filter_by(user_id=current_user.id).all()
custom_domains = CustomDomain.filter_by(user_id=current_user.id).all()
mailboxes = current_user.mailboxes()
new_custom_domain_form = NewCustomDomainForm()
@ -54,7 +54,7 @@ def custom_domain():
new_custom_domain = CustomDomain.create(
domain=new_domain, user_id=current_user.id
)
db.session.commit()
Session.commit()
mailbox_ids = request.form.getlist("mailbox_ids")
if mailbox_ids:
@ -76,7 +76,7 @@ def custom_domain():
domain_id=new_custom_domain.id, mailbox_id=mailbox.id
)
db.session.commit()
Session.commit()
flash(
f"New domain {new_custom_domain.domain} is created", "success"

View File

@ -10,7 +10,7 @@ from app.config import (
BOUNCE_PREFIX_FOR_REPLY_PHASE,
)
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.models import Directory, Mailbox, DirectoryMailbox
@ -24,7 +24,7 @@ class NewDirForm(FlaskForm):
@login_required
def directory():
dirs = (
Directory.query.filter_by(user_id=current_user.id)
Directory.filter_by(user_id=current_user.id)
.order_by(Directory.created_at.desc())
.all()
)
@ -47,7 +47,7 @@ def directory():
name = dir.name
Directory.delete(dir_id)
db.session.commit()
Session.commit()
flash(f"Directory {name} has been deleted", "success")
return redirect(url_for("dashboard.directory"))
@ -67,7 +67,7 @@ def directory():
dir.disabled = True
flash(f"On-the-fly is disabled for {dir.name}", "warning")
db.session.commit()
Session.commit()
return redirect(url_for("dashboard.directory"))
@ -98,13 +98,13 @@ def directory():
return redirect(url_for("dashboard.directory"))
# first remove all existing directory-mailboxes links
DirectoryMailbox.query.filter_by(directory_id=dir.id).delete()
db.session.flush()
DirectoryMailbox.filter_by(directory_id=dir.id).delete()
Session.flush()
for mailbox in mailboxes:
DirectoryMailbox.create(directory_id=dir.id, mailbox_id=mailbox.id)
db.session.commit()
Session.commit()
flash(f"Directory {dir.name} has been updated", "success")
return redirect(url_for("dashboard.directory"))
@ -141,7 +141,7 @@ def directory():
new_dir = Directory.create(
name=new_dir_name, user_id=current_user.id
)
db.session.commit()
Session.commit()
mailbox_ids = request.form.getlist("mailbox_ids")
if mailbox_ids:
# check if mailbox is not tempered with
@ -162,7 +162,7 @@ def directory():
directory_id=new_dir.id, mailbox_id=mailbox.id
)
db.session.commit()
Session.commit()
flash(f"Directory {new_dir.name} is created", "success")

View File

@ -1,6 +1,6 @@
import re2 as re
from threading import Thread
import re2 as re
from flask import render_template, request, redirect, url_for, flash
from flask_login import login_required, current_user
from flask_wtf import FlaskForm
@ -8,6 +8,7 @@ from wtforms import StringField, validators, IntegerField
from app.config import EMAIL_SERVERS_WITH_PRIORITY, EMAIL_DOMAIN
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.dns_utils import (
get_mx_domains,
get_spf_domain,
@ -15,7 +16,6 @@ from app.dns_utils import (
get_cname_record,
)
from app.email_utils import send_email
from app.extensions import db
from app.log import LOG
from app.models import (
CustomDomain,
@ -40,7 +40,7 @@ def domain_detail_dns(custom_domain_id):
# generate a domain ownership txt token if needed
if not custom_domain.ownership_verified and not custom_domain.ownership_txt_token:
custom_domain.ownership_txt_token = random_string(30)
db.session.commit()
Session.commit()
spf_record = f"v=spf1 include:{EMAIL_DOMAIN} ~all"
@ -62,7 +62,7 @@ def domain_detail_dns(custom_domain_id):
"success",
)
custom_domain.ownership_verified = True
db.session.commit()
Session.commit()
return redirect(
url_for(
"dashboard.domain_detail_dns",
@ -92,7 +92,7 @@ def domain_detail_dns(custom_domain_id):
"success",
)
custom_domain.verified = True
db.session.commit()
Session.commit()
return redirect(
url_for(
"dashboard.domain_detail_dns", custom_domain_id=custom_domain.id
@ -102,7 +102,7 @@ def domain_detail_dns(custom_domain_id):
spf_domains = get_spf_domain(custom_domain.domain)
if EMAIL_DOMAIN in spf_domains:
custom_domain.spf_verified = True
db.session.commit()
Session.commit()
flash("SPF is setup correctly", "success")
return redirect(
url_for(
@ -111,7 +111,7 @@ def domain_detail_dns(custom_domain_id):
)
else:
custom_domain.spf_verified = False
db.session.commit()
Session.commit()
flash(
f"SPF: {EMAIL_DOMAIN} is not included in your SPF record.",
"warning",
@ -124,7 +124,7 @@ def domain_detail_dns(custom_domain_id):
if dkim_record == dkim_cname:
flash("DKIM is setup correctly.", "success")
custom_domain.dkim_verified = True
db.session.commit()
Session.commit()
return redirect(
url_for(
@ -133,7 +133,7 @@ def domain_detail_dns(custom_domain_id):
)
else:
custom_domain.dkim_verified = False
db.session.commit()
Session.commit()
flash("DKIM: the CNAME record is not correctly set", "warning")
dkim_ok = False
dkim_errors = [dkim_record or "[Empty]"]
@ -142,7 +142,7 @@ def domain_detail_dns(custom_domain_id):
txt_records = get_txt_record("_dmarc." + custom_domain.domain)
if dmarc_record in txt_records:
custom_domain.dmarc_verified = True
db.session.commit()
Session.commit()
flash("DMARC is setup correctly", "success")
return redirect(
url_for(
@ -151,7 +151,7 @@ def domain_detail_dns(custom_domain_id):
)
else:
custom_domain.dmarc_verified = False
db.session.commit()
Session.commit()
flash(
"DMARC: The TXT record is not correctly set",
"warning",
@ -179,7 +179,7 @@ def domain_detail(custom_domain_id):
if request.method == "POST":
if request.form.get("form-name") == "switch-catch-all":
custom_domain.catch_all = not custom_domain.catch_all
db.session.commit()
Session.commit()
if custom_domain.catch_all:
flash(
@ -197,14 +197,14 @@ def domain_detail(custom_domain_id):
elif request.form.get("form-name") == "set-name":
if request.form.get("action") == "save":
custom_domain.name = request.form.get("alias-name").replace("\n", "")
db.session.commit()
Session.commit()
flash(
f"Default alias name for Domain {custom_domain.domain} has been set",
"success",
)
else:
custom_domain.name = None
db.session.commit()
Session.commit()
flash(
f"Default alias name for Domain {custom_domain.domain} has been removed",
"info",
@ -217,7 +217,7 @@ def domain_detail(custom_domain_id):
custom_domain.random_prefix_generation = (
not custom_domain.random_prefix_generation
)
db.session.commit()
Session.commit()
if custom_domain.random_prefix_generation:
flash(
@ -260,13 +260,13 @@ def domain_detail(custom_domain_id):
)
# first remove all existing domain-mailboxes links
DomainMailbox.query.filter_by(domain_id=custom_domain.id).delete()
db.session.flush()
DomainMailbox.filter_by(domain_id=custom_domain.id).delete()
Session.flush()
for mailbox in mailboxes:
DomainMailbox.create(domain_id=custom_domain.id, mailbox_id=mailbox.id)
db.session.commit()
Session.commit()
flash(f"{custom_domain.domain} mailboxes has been updated", "success")
return redirect(
@ -302,7 +302,7 @@ def delete_domain(custom_domain_id: int):
user = custom_domain.user
CustomDomain.delete(custom_domain.id)
db.session.commit()
Session.commit()
LOG.d("Domain %s deleted", domain_name)
@ -328,7 +328,7 @@ def domain_detail_trash(custom_domain_id):
if request.method == "POST":
if request.form.get("form-name") == "empty-all":
DomainDeletedAlias.filter_by(domain_id=custom_domain.id).delete()
db.session.commit()
Session.commit()
flash("All deleted aliases can now be re-created", "success")
return redirect(
@ -349,7 +349,7 @@ def domain_detail_trash(custom_domain_id):
)
DomainDeletedAlias.delete(deleted_alias.id)
db.session.commit()
Session.commit()
flash(
f"{deleted_alias.email} can now be re-created",
"success",
@ -477,7 +477,7 @@ def domain_detail_auto_create(custom_domain_id):
auto_create_rule_id=rule.id, mailbox_id=mailbox.id
)
db.session.commit()
Session.commit()
flash("New auto create rule has been created", "success")
@ -502,7 +502,7 @@ def domain_detail_auto_create(custom_domain_id):
rule_order = rule.order
AutoCreateRule.delete(rule_id)
db.session.commit()
Session.commit()
flash(f"Rule #{rule_order} has been deleted", "success")
return redirect(
url_for(

View File

@ -5,7 +5,7 @@ from wtforms import HiddenField, validators
from app.dashboard.base import dashboard_bp
from app.dashboard.views.enter_sudo import sudo_required
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import RecoveryCode, Fido
@ -34,7 +34,7 @@ def fido_manage():
return redirect(url_for("dashboard.fido_manage"))
Fido.delete(fido_key.id)
db.session.commit()
Session.commit()
LOG.d(f"FIDO Key ID={fido_key.id} Removed")
flash(f"Key {fido_key.name} successfully unlinked", "success")
@ -42,7 +42,7 @@ def fido_manage():
# Disable FIDO for the user if all keys have been deleted
if not Fido.filter_by(uuid=current_user.fido_uuid).all():
current_user.fido_uuid = None
db.session.commit()
Session.commit()
# user does not have any 2FA enabled left, delete all recovery codes
if not current_user.two_factor_authentication_enabled():

View File

@ -11,7 +11,7 @@ from wtforms import StringField, HiddenField, validators
from app.config import RP_ID, URL
from app.dashboard.base import dashboard_bp
from app.dashboard.views.enter_sudo import sudo_required
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import Fido, RecoveryCode
@ -61,7 +61,7 @@ def fido_setup():
if current_user.fido_uuid is None:
current_user.fido_uuid = fido_uuid
db.session.flush()
Session.flush()
Fido.create(
credential_id=str(fido_credential.credential_id, "utf-8"),
@ -70,14 +70,14 @@ def fido_setup():
sign_count=fido_credential.sign_count,
name=fido_token_form.key_name.data,
)
db.session.commit()
Session.commit()
LOG.d(
f"credential_id={str(fido_credential.credential_id, 'utf-8')} added for {fido_uuid}"
)
flash("Security key has been activated", "success")
if not RecoveryCode.query.filter_by(user_id=current_user.id).all():
if not RecoveryCode.filter_by(user_id=current_user.id).all():
return redirect(url_for("dashboard.recovery_code_route"))
else:
return redirect(url_for("dashboard.fido_manage"))

View File

@ -7,7 +7,8 @@ from app import alias_utils
from app.api.serializer import get_alias_infos_with_pagination_v3, get_alias_info_v3
from app.config import PAGE_LIMIT, ALIAS_LIMIT
from app.dashboard.base import dashboard_bp
from app.extensions import db, limiter
from app.db import Session
from app.extensions import limiter
from app.log import LOG
from app.models import (
Alias,
@ -26,19 +27,19 @@ class Stats:
def get_stats(user: User) -> Stats:
nb_alias = Alias.query.filter_by(user_id=user.id).count()
nb_alias = Alias.filter_by(user_id=user.id).count()
nb_forward = (
db.session.query(EmailLog)
Session.query(EmailLog)
.filter_by(user_id=user.id, is_reply=False, blocked=False, bounced=False)
.count()
)
nb_reply = (
db.session.query(EmailLog)
Session.query(EmailLog)
.filter_by(user_id=user.id, is_reply=True, blocked=False, bounced=False)
.count()
)
nb_block = (
db.session.query(EmailLog)
Session.query(EmailLog)
.filter_by(user_id=user.id, is_reply=False, blocked=True, bounced=False)
.count()
)
@ -92,7 +93,7 @@ def index():
alias.mailbox_id = current_user.default_mailbox_id
db.session.commit()
Session.commit()
LOG.d("create new random alias %s for user %s", alias, current_user)
flash(f"Alias {alias.email} has been created", "success")
@ -130,7 +131,7 @@ def index():
flash(f"Alias {email} has been deleted", "success")
elif request.form.get("form-name") == "disable-alias":
alias.enabled = False
db.session.commit()
Session.commit()
flash(f"Alias {alias.email} has been disabled", "success")
return redirect(
@ -146,7 +147,7 @@ def index():
# to make sure not showing intro to user again
current_user.intro_shown = True
db.session.commit()
Session.commit()
stats = get_stats(current_user)

View File

@ -5,8 +5,8 @@ from wtforms import StringField, validators
from app.config import ADMIN_EMAIL
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import send_email
from app.extensions import db
from app.models import LifetimeCoupon
@ -40,7 +40,7 @@ def lifetime_licence():
current_user.lifetime_coupon_id = coupon.id
if coupon.paid:
current_user.paid_lifetime = True
db.session.commit()
Session.commit()
# notify admin
send_email(

View File

@ -9,6 +9,7 @@ from wtforms.fields.html5 import EmailField
from app.config import MAILBOX_SECRET, URL
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import (
email_can_be_used_as_mailbox,
mailbox_already_used,
@ -16,7 +17,6 @@ from app.email_utils import (
send_email,
is_valid_email,
)
from app.extensions import db
from app.log import LOG
from app.models import Mailbox
@ -31,7 +31,7 @@ class NewMailboxForm(FlaskForm):
@login_required
def mailbox_route():
mailboxes = (
Mailbox.query.filter_by(user_id=current_user.id)
Mailbox.filter_by(user_id=current_user.id)
.order_by(Mailbox.created_at.desc())
.all()
)
@ -77,7 +77,7 @@ def mailbox_route():
return redirect(url_for("dashboard.mailbox_route"))
current_user.default_mailbox_id = mailbox.id
db.session.commit()
Session.commit()
flash(f"Mailbox {mailbox.email} is set as Default Mailbox", "success")
return redirect(url_for("dashboard.mailbox_route"))
@ -102,7 +102,7 @@ def mailbox_route():
new_mailbox = Mailbox.create(
email=mailbox_email, user_id=current_user.id
)
db.session.commit()
Session.commit()
send_verification_email(current_user, new_mailbox)
@ -136,7 +136,7 @@ def delete_mailbox(mailbox_id: int):
user = mailbox.user
Mailbox.delete(mailbox_id)
db.session.commit()
Session.commit()
LOG.d("Mailbox %s %s deleted", mailbox_id, mailbox_email)
send_email(
@ -191,7 +191,7 @@ def mailbox_verify():
return redirect(url_for("dashboard.mailbox_route"))
mailbox.verified = True
db.session.commit()
Session.commit()
LOG.d("Mailbox %s is verified", mailbox)

View File

@ -10,9 +10,9 @@ from wtforms.fields.html5 import EmailField
from app.config import ENFORCE_SPF, MAILBOX_SECRET
from app.config import URL
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import email_can_be_used_as_mailbox
from app.email_utils import mailbox_already_used, render, send_email
from app.extensions import db
from app.log import LOG
from app.models import Alias, AuthorizedAddress
from app.models import Mailbox
@ -57,7 +57,7 @@ def mailbox_detail_route(mailbox_id):
flash("You cannot use this email address as your mailbox", "error")
else:
mailbox.new_email = new_email
db.session.commit()
Session.commit()
try:
verify_mailbox_change(current_user, mailbox, new_email)
@ -82,7 +82,7 @@ def mailbox_detail_route(mailbox_id):
mailbox.force_spf = (
True if request.form.get("spf-status") == "on" else False
)
db.session.commit()
Session.commit()
flash(
"SPF enforcement was " + "enabled"
if request.form.get("spf-status")
@ -118,7 +118,7 @@ def mailbox_detail_route(mailbox_id):
else:
address = authorized_address.email
AuthorizedAddress.delete(authorized_address_id)
db.session.commit()
Session.commit()
flash(f"{address} has been deleted", "success")
return redirect(
@ -140,7 +140,7 @@ def mailbox_detail_route(mailbox_id):
except PGPException:
flash("Cannot add the public key, please verify it", "error")
else:
db.session.commit()
Session.commit()
flash("Your PGP public key is saved successfully", "success")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
@ -150,7 +150,7 @@ def mailbox_detail_route(mailbox_id):
mailbox.pgp_public_key = None
mailbox.pgp_finger_print = None
mailbox.disable_pgp = False
db.session.commit()
Session.commit()
flash("Your PGP public key is removed successfully", "success")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
@ -164,7 +164,7 @@ def mailbox_detail_route(mailbox_id):
mailbox.disable_pgp = True
flash(f"PGP is disabled on {mailbox.email}", "info")
db.session.commit()
Session.commit()
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
)
@ -180,14 +180,14 @@ def mailbox_detail_route(mailbox_id):
)
mailbox.generic_subject = request.form.get("generic-subject")
db.session.commit()
Session.commit()
flash("Generic subject for PGP-encrypted email is enabled", "success")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
)
elif request.form.get("action") == "remove":
mailbox.generic_subject = None
db.session.commit()
Session.commit()
flash("Generic subject for PGP-encrypted email is disabled", "success")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
@ -236,7 +236,7 @@ def cancel_mailbox_change_route(mailbox_id):
if mailbox.new_email:
mailbox.new_email = None
db.session.commit()
Session.commit()
flash("Your mailbox change is cancelled", "success")
return redirect(
url_for("dashboard.mailbox_detail_route", mailbox_id=mailbox_id)
@ -274,7 +274,7 @@ def mailbox_confirm_change_route():
# mark mailbox as verified if the change request is sent from an unverified mailbox
mailbox.verified = True
db.session.commit()
Session.commit()
LOG.d("Mailbox change %s is verified", mailbox)
flash(f"The {mailbox.email} is updated", "success")

View File

@ -3,7 +3,7 @@ from flask_login import login_required, current_user
from app.dashboard.base import dashboard_bp
from app.dashboard.views.enter_sudo import sudo_required
from app.extensions import db
from app.db import Session
from app.models import RecoveryCode
@ -19,7 +19,7 @@ def mfa_cancel():
if request.method == "POST":
current_user.enable_otp = False
current_user.otp_secret = None
db.session.commit()
Session.commit()
# user does not have any 2FA enabled left, delete all recovery codes
if not current_user.two_factor_authentication_enabled():

View File

@ -6,7 +6,7 @@ from wtforms import StringField, validators
from app.dashboard.base import dashboard_bp
from app.dashboard.views.enter_sudo import sudo_required
from app.extensions import db
from app.db import Session
from app.log import LOG
@ -27,7 +27,7 @@ def mfa_setup():
if not current_user.otp_secret:
LOG.d("Generate otp_secret for user %s", current_user)
current_user.otp_secret = pyotp.random_base32()
db.session.commit()
Session.commit()
totp = pyotp.TOTP(current_user.otp_secret)
@ -37,7 +37,7 @@ def mfa_setup():
if totp.verify(token) and current_user.last_otp != token:
current_user.enable_otp = True
current_user.last_otp = token
db.session.commit()
Session.commit()
flash("MFA has been activated", "success")
return redirect(url_for("dashboard.recovery_code_route"))

View File

@ -13,12 +13,12 @@ def recovery_code_route():
flash("you need to enable either TOTP or WebAuthn", "warning")
return redirect(url_for("dashboard.index"))
recovery_codes = RecoveryCode.query.filter_by(user_id=current_user.id).all()
recovery_codes = RecoveryCode.filter_by(user_id=current_user.id).all()
if request.method == "GET" and not recovery_codes:
# user arrives at this page for the first time
LOG.d("%s has no recovery keys, generate", current_user)
RecoveryCode.generate(current_user)
recovery_codes = RecoveryCode.query.filter_by(user_id=current_user.id).all()
recovery_codes = RecoveryCode.filter_by(user_id=current_user.id).all()
if request.method == "POST":
RecoveryCode.generate(current_user)

View File

@ -1,10 +1,9 @@
import re2 as re
from flask import render_template, request, flash, redirect, url_for
from flask_login import login_required, current_user
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.db import Session
from app.models import Referral, Payout
_REFERRAL_PATTERN = r"[0-9a-z-_]{3,}"
@ -30,7 +29,7 @@ def referral_route():
name = request.form.get("name")
referral = Referral.create(user_id=current_user.id, code=code, name=name)
db.session.commit()
Session.commit()
flash("A new referral code has been created", "success")
return redirect(
url_for("dashboard.referral_route", highlight_id=referral.id)
@ -40,7 +39,7 @@ def referral_route():
referral = Referral.get(referral_id)
if referral and referral.user_id == current_user.id:
referral.name = request.form.get("name")
db.session.commit()
Session.commit()
flash("Referral name updated", "success")
return redirect(
url_for("dashboard.referral_route", highlight_id=referral.id)
@ -50,7 +49,7 @@ def referral_route():
referral = Referral.get(referral_id)
if referral and referral.user_id == current_user.id:
Referral.delete(referral.id)
db.session.commit()
Session.commit()
flash("Referral deleted", "success")
return redirect(url_for("dashboard.referral_route"))
@ -59,7 +58,7 @@ def referral_route():
if highlight_id:
highlight_id = int(highlight_id)
referrals = Referral.query.filter_by(user_id=current_user.id).all()
referrals = Referral.filter_by(user_id=current_user.id).all()
# make sure the highlighted referral is the first referral
highlight_index = None
for ix, referral in enumerate(referrals):
@ -70,6 +69,6 @@ def referral_route():
if highlight_index:
referrals.insert(0, referrals.pop(highlight_index))
payouts = Payout.query.filter_by(user_id=current_user.id).all()
payouts = Payout.filter_by(user_id=current_user.id).all()
return render_template("dashboard/referral.html", **locals())

View File

@ -19,7 +19,7 @@ def refused_email_route():
highlight_id = None
email_logs: [EmailLog] = (
EmailLog.query.filter(
EmailLog.filter(
EmailLog.user_id == current_user.id, EmailLog.refused_email_id.isnot(None)
)
.order_by(EmailLog.id.desc())

View File

@ -22,11 +22,11 @@ from app.config import (
ALIAS_RANDOM_SUFFIX_LENGTH,
)
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.email_utils import (
email_can_be_used_as_mailbox,
personal_email_already_used,
)
from app.extensions import db
from app.log import LOG
from app.models import (
PlanEnum,
@ -116,7 +116,7 @@ def setting():
"delete the expired email change %s", other_email_change
)
EmailChange.delete(other_email_change.id)
db.session.commit()
Session.commit()
else:
flash(
"You cannot use this email address as your personal inbox.",
@ -132,7 +132,7 @@ def setting():
), # todo: make sure the code is unique
new_email=new_email,
)
db.session.commit()
Session.commit()
send_change_email_confirmation(current_user, email_change)
flash(
"A confirmation email is on the way, please check your inbox",
@ -145,7 +145,7 @@ def setting():
# update user info
if form.name.data != current_user.name:
current_user.name = form.name.data
db.session.commit()
Session.commit()
profile_updated = True
if form.profile_picture.data:
@ -156,11 +156,11 @@ def setting():
file_path, BytesIO(form.profile_picture.data.read())
)
db.session.flush()
Session.flush()
LOG.d("upload file %s to s3", file)
current_user.profile_picture_id = file.id
db.session.commit()
Session.commit()
profile_updated = True
if profile_updated:
@ -181,7 +181,7 @@ def setting():
current_user.notification = True
else:
current_user.notification = False
db.session.commit()
Session.commit()
flash("Your notification preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -212,7 +212,7 @@ def setting():
scheme = int(request.form.get("alias-generator-scheme"))
if AliasGeneratorEnum.has_value(scheme):
current_user.alias_generator = scheme
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -249,7 +249,7 @@ def setting():
current_user.default_alias_custom_domain_id = None
current_user.default_alias_public_domain_id = None
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -257,7 +257,7 @@ def setting():
scheme = int(request.form.get("random-alias-suffix-generator"))
if AliasSuffixEnum.has_value(scheme):
current_user.random_alias_suffix = scheme
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -266,9 +266,9 @@ def setting():
if SenderFormatEnum.has_value(sender_format):
current_user.sender_format = sender_format
current_user.sender_format_updated_at = arrow.now()
db.session.commit()
Session.commit()
flash("Your sender format preference has been updated", "success")
db.session.commit()
Session.commit()
return redirect(url_for("dashboard.setting"))
elif request.form.get("form-name") == "replace-ra":
@ -277,7 +277,7 @@ def setting():
current_user.replace_reverse_alias = True
else:
current_user.replace_reverse_alias = False
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -287,7 +287,7 @@ def setting():
current_user.include_sender_in_reverse_alias = True
else:
current_user.include_sender_in_reverse_alias = False
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -297,7 +297,7 @@ def setting():
current_user.expand_alias_info = True
else:
current_user.expand_alias_info = False
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
elif request.form.get("form-name") == "ignore-loop-email":
@ -306,7 +306,7 @@ def setting():
current_user.ignore_loop_email = True
else:
current_user.ignore_loop_email = False
db.session.commit()
Session.commit()
flash("Your preference has been updated", "success")
return redirect(url_for("dashboard.setting"))
@ -344,7 +344,7 @@ def send_reset_password_email(user):
reset_password_code = ResetPasswordCode.create(
user_id=user.id, code=random_string(60)
)
db.session.commit()
Session.commit()
reset_password_link = f"{URL}/auth/reset_password?code={reset_password_code.code}"
@ -368,7 +368,7 @@ def resend_email_change():
if email_change:
# extend email change expiration
email_change.expired = arrow.now().shift(hours=12)
db.session.commit()
Session.commit()
send_change_email_confirmation(current_user, email_change)
flash("A confirmation email is on the way, please check your inbox", "success")
@ -386,7 +386,7 @@ def cancel_email_change():
email_change = EmailChange.get_by(user_id=current_user.id)
if email_change:
EmailChange.delete(email_change.id)
db.session.commit()
Session.commit()
flash("Your email change is cancelled", "success")
return redirect(url_for("dashboard.setting"))
else:

View File

@ -1,3 +1,5 @@
from app.db import Session
"""
Allow user to "unsubscribe", aka block an email alias
"""
@ -6,7 +8,6 @@ from flask import redirect, url_for, flash, request, render_template
from flask_login import login_required, current_user
from app.dashboard.base import dashboard_bp
from app.extensions import db
from app.models import Alias
@ -29,7 +30,7 @@ def unsubscribe(alias_id):
if request.method == "POST":
alias.enabled = False
flash(f"Alias {alias.email} has been blocked", "success")
db.session.commit()
Session.commit()
return redirect(url_for("dashboard.index", highlight_alias_id=alias.id))
else: # ask user confirmation

10
app/db.py Normal file
View File

@ -0,0 +1,10 @@
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session
from sqlalchemy.orm import sessionmaker
from app.config import DB_URI
engine = create_engine(DB_URI)
connection = engine.connect()
Session = scoped_session(sessionmaker(bind=connection))

View File

@ -8,9 +8,9 @@ from wtforms import StringField, validators, TextAreaField
from app import s3
from app.config import ADMIN_EMAIL
from app.db import Session
from app.developer.base import developer_bp
from app.email_utils import send_email
from app.extensions import db
from app.log import LOG
from app.models import Client, RedirectUri, File
from app.utils import random_string
@ -55,13 +55,13 @@ def client_detail(client_id):
s3.upload_from_bytesio(file_path, BytesIO(form.icon.data.read()))
db.session.flush()
Session.flush()
LOG.d("upload file %s to s3", file)
client.icon_id = file.id
db.session.flush()
Session.flush()
db.session.commit()
Session.commit()
flash(f"{client.name} has been updated", "success")
@ -69,7 +69,7 @@ def client_detail(client_id):
if action == "submit" and approval_form.validate_on_submit():
client.description = approval_form.description.data
db.session.commit()
Session.commit()
send_email(
ADMIN_EMAIL,
@ -127,7 +127,7 @@ def client_detail_oauth_setting(client_id):
for uri in uris:
RedirectUri.create(client_id=client_id, uri=uri)
db.session.commit()
Session.commit()
flash(f"{client.name} has been updated", "success")
@ -178,7 +178,7 @@ def client_detail_advanced(client_id):
# delete client
client_name = client.name
Client.delete(client.id)
db.session.commit()
Session.commit()
LOG.d("Remove client %s", client)
flash(f"{client_name} has been deleted", "success")

View File

@ -3,8 +3,8 @@ from flask_login import current_user, login_required
from flask_wtf import FlaskForm
from wtforms import StringField, validators
from app.db import Session
from app.developer.base import developer_bp
from app.extensions import db
from app.models import Client
@ -19,7 +19,7 @@ def new_client():
if form.validate_on_submit():
client = Client.create_new(form.name.data, current_user.id)
db.session.commit()
Session.commit()
flash("Your app has been created", "success")

View File

@ -5,8 +5,8 @@ from app.config import (
MAX_ACTIVITY_DURING_MINUTE_PER_ALIAS,
MAX_ACTIVITY_DURING_MINUTE_PER_MAILBOX,
)
from app.db import Session
from app.email_utils import is_reply_email
from app.extensions import db
from app.log import LOG
from app.models import Alias, EmailLog, Contact
@ -16,7 +16,7 @@ def rate_limited_for_alias(alias: Alias) -> bool:
# get the nb of activity on this alias
nb_activity = (
db.session.query(EmailLog)
Session.query(EmailLog)
.join(Contact, EmailLog.contact_id == Contact.id)
.filter(
Contact.alias_id == alias.id,
@ -42,7 +42,7 @@ def rate_limited_for_mailbox(alias: Alias) -> bool:
# get nb of activity on this mailbox
nb_activity = (
db.session.query(EmailLog)
Session.query(EmailLog)
.join(Contact, EmailLog.contact_id == Contact.id)
.join(Alias, Contact.alias_id == Alias.id)
.filter(

View File

@ -53,9 +53,9 @@ from app.config import (
TEMP_DIR,
ALIAS_AUTOMATIC_DISABLE,
)
from app.db import Session
from app.dns_utils import get_mx_domains
from app.email import headers
from app.extensions import db
from app.log import LOG
from app.models import (
Mailbox,
@ -324,7 +324,7 @@ def send_email_with_rate_control(
to_email = sanitize_email(to_email)
min_dt = arrow.now().shift(days=-1 * nb_day)
nb_alert = (
SentAlert.query.filter_by(alert_type=alert_type, to_email=to_email)
SentAlert.filter_by(alert_type=alert_type, to_email=to_email)
.filter(SentAlert.created_at > min_dt)
.count()
)
@ -340,7 +340,7 @@ def send_email_with_rate_control(
return False
SentAlert.create(user_id=user.id, alert_type=alert_type, to_email=to_email)
db.session.commit()
Session.commit()
if ignore_smtp_error:
try:
@ -369,9 +369,7 @@ def send_email_at_most_times(
Return true if the email is sent, otherwise False
"""
to_email = sanitize_email(to_email)
nb_alert = SentAlert.query.filter_by(
alert_type=alert_type, to_email=to_email
).count()
nb_alert = SentAlert.filter_by(alert_type=alert_type, to_email=to_email).count()
if nb_alert >= max_times:
LOG.w(
@ -383,7 +381,7 @@ def send_email_at_most_times(
return False
SentAlert.create(user_id=user.id, alert_type=alert_type, to_email=to_email)
db.session.commit()
Session.commit()
send_email(to_email, subject, plaintext, html)
return True
@ -1036,7 +1034,7 @@ def should_disable(alias: Alias) -> bool:
yesterday = arrow.now().shift(days=-1)
nb_bounced_last_24h = (
db.session.query(EmailLog)
Session.query(EmailLog)
.filter(
EmailLog.bounced.is_(True),
EmailLog.is_reply.is_(False),
@ -1054,7 +1052,7 @@ def should_disable(alias: Alias) -> bool:
elif nb_bounced_last_24h > 5:
one_week_ago = arrow.now().shift(days=-8)
nb_bounced_7d_1d = (
db.session.query(EmailLog)
Session.query(EmailLog)
.filter(
EmailLog.bounced.is_(True),
EmailLog.is_reply.is_(False),
@ -1075,7 +1073,7 @@ def should_disable(alias: Alias) -> bool:
# alias level
# if bounces at least 9 days in the last 10 days -> disable alias
query = (
db.session.query(
Session.query(
func.date(EmailLog.created_at).label("date"),
func.count(EmailLog.id).label("count"),
)
@ -1097,7 +1095,7 @@ def should_disable(alias: Alias) -> bool:
# account level
query = (
db.session.query(
Session.query(
func.date(EmailLog.created_at).label("date"),
func.count(EmailLog.id).label("count"),
)

View File

@ -3,8 +3,8 @@ import csv
import requests
from app import s3
from app.db import Session
from app.email_utils import get_email_domain_part
from app.extensions import db
from app.models import (
Alias,
AliasMailbox,
@ -23,7 +23,7 @@ def handle_batch_import(batch_import: BatchImport):
user = batch_import.user
batch_import.processed = True
db.session.commit()
Session.commit()
LOG.d("Start batch import for %s %s", batch_import, user)
file_url = s3.get_url(batch_import.file.path)
@ -97,5 +97,5 @@ def import_from_csv(batch_import: BatchImport, user: User, lines):
AliasMailbox.create(
alias_id=alias.id, mailbox_id=mailboxes[i], commit=True
)
db.session.commit()
Session.commit()
LOG.d("Add %s to mailbox %s", alias, mailboxes[i])

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ from itsdangerous import SignatureExpired
from app.alias_utils import check_alias_prefix
from app.config import EMAIL_DOMAIN
from app.dashboard.views.custom_alias import signer, get_available_suffixes
from app.extensions import db
from app.db import Session
from app.jose_utils import make_id_token
from app.log import LOG
from app.models import (
@ -206,7 +206,7 @@ def authorize():
if domain:
alias.custom_domain_id = domain.id
db.session.flush()
Session.flush()
flash(f"Alias {full_alias} has been created", "success")
# only happen if the request has been "hacked"
else:
@ -224,7 +224,7 @@ def authorize():
user_id=current_user.id,
mailbox_id=current_user.default_mailbox_id,
)
db.session.flush()
Session.flush()
suggested_name = request.form.get("suggested-name")
custom_name = request.form.get("custom-name")
@ -247,7 +247,7 @@ def authorize():
LOG.d("use default avatar for user %s client %s", current_user, client)
client_user.default_avatar = True
db.session.flush()
Session.flush()
LOG.d("create client-user for client %s, user %s", client, current_user)
redirect_args = {}
@ -284,7 +284,7 @@ def authorize():
access_token=generate_access_token(),
response_type=response_types_to_str(response_types),
)
db.session.add(oauth_token)
Session.add(oauth_token)
redirect_args["access_token"] = oauth_token.access_token
if ResponseType.ID_TOKEN in response_types:
@ -295,7 +295,7 @@ def authorize():
auth_code.code if auth_code else None,
)
db.session.commit()
Session.commit()
# should all params appended the url using fragment (#) or query
fragment = False

View File

@ -1,7 +1,7 @@
from flask import request, jsonify
from flask_cors import cross_origin
from app.extensions import db
from app.db import Session
from app.jose_utils import make_id_token
from app.log import LOG
from app.models import Client, AuthorizationCode, OauthToken, ClientUser
@ -49,7 +49,7 @@ def token():
return jsonify(error=f"no such authorization code {code}"), 400
elif auth_code.is_expired():
AuthorizationCode.delete(auth_code.id)
db.session.commit()
Session.commit()
LOG.d("delete expired authorization code:%s", auth_code)
return jsonify(error=f"{code} already expired"), 400
@ -94,6 +94,6 @@ def token():
# Auth code can be used only once
AuthorizationCode.delete(auth_code.id)
db.session.commit()
Session.commit()
return jsonify(res)

View File

@ -1,7 +1,7 @@
from flask import request, jsonify
from flask_cors import cross_origin
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import OauthToken, ClientUser
from app.oauth.base import oauth_bp
@ -27,7 +27,7 @@ def user_info():
elif oauth_token.is_expired():
LOG.d("delete oauth token %s", oauth_token)
OauthToken.delete(oauth_token.id)
db.session.commit()
Session.commit()
return jsonify(error="Expired access token"), 400
client_user = ClientUser.get_or_create(

View File

@ -1,14 +1,12 @@
import unicodedata
import bcrypt
from app.extensions import db
import sqlalchemy as sa
import unicodedata
_NORMALIZATION_FORM = "NFKC"
class PasswordOracle:
password = db.Column(db.String(128), nullable=True)
password = sa.Column(sa.String(128), nullable=True)
def set_password(self, password):
password = unicodedata.normalize(_NORMALIZATION_FORM, password)

View File

@ -2,11 +2,12 @@
https://github.com/petermat/spamassassin_client
"""
import logging
import re2 as re
import select
import socket
from io import BytesIO
import re2 as re
import select
from app.log import LOG
divider_pattern = re.compile(br"^(.*?)\r?\n(.*?)\r?\n\r?\n", re.DOTALL)

122
cron.py
View File

@ -22,6 +22,7 @@ from app.config import (
HIBP_API_KEYS,
HIBP_SCAN_INTERVAL_DAYS,
)
from app.db import Session
from app.dns_utils import get_mx_domains
from app.email_utils import (
send_email,
@ -33,7 +34,6 @@ from app.email_utils import (
is_valid_email,
get_email_domain_part,
)
from app.extensions import db
from app.log import LOG
from app.models import (
Subscription,
@ -63,7 +63,7 @@ from server import create_app
def notify_trial_end():
for user in User.query.filter(
for user in User.filter(
User.activated.is_(True), User.trial_end.isnot(None), User.lifetime.is_(False)
).all():
if user.in_trial() and arrow.now().shift(
@ -78,27 +78,27 @@ def delete_logs():
delete_refused_emails()
delete_old_monitoring()
for t in TransactionalEmail.query.filter(
for t in TransactionalEmail.filter(
TransactionalEmail.created_at < arrow.now().shift(days=-7)
):
TransactionalEmail.delete(t.id)
for b in Bounce.query.filter(Bounce.created_at < arrow.now().shift(days=-7)):
for b in Bounce.filter(Bounce.created_at < arrow.now().shift(days=-7)):
Bounce.delete(b.id)
db.session.commit()
Session.commit()
LOG.d("Delete EmailLog older than 2 weeks")
max_dt = arrow.now().shift(weeks=-2)
nb_deleted = EmailLog.query.filter(EmailLog.created_at < max_dt).delete()
db.session.commit()
nb_deleted = EmailLog.filter(EmailLog.created_at < max_dt).delete()
Session.commit()
LOG.i("Delete %s email logs", nb_deleted)
def delete_refused_emails():
for refused_email in RefusedEmail.query.filter_by(deleted=False).all():
for refused_email in RefusedEmail.filter_by(deleted=False).all():
if arrow.now().shift(days=1) > refused_email.delete_at >= arrow.now():
LOG.d("Delete refused email %s", refused_email)
if refused_email.path:
@ -109,14 +109,14 @@ def delete_refused_emails():
# do not set path and full_report_path to null
# so we can check later that the files are indeed deleted
refused_email.deleted = True
db.session.commit()
Session.commit()
LOG.d("Finish delete_refused_emails")
def notify_premium_end():
"""sent to user who has canceled their subscription and who has their subscription ending soon"""
for sub in Subscription.query.filter_by(cancelled=True).all():
for sub in Subscription.filter_by(cancelled=True).all():
if (
arrow.now().shift(days=3).date()
> sub.next_bill_date
@ -146,7 +146,7 @@ def notify_premium_end():
def notify_manual_sub_end():
for manual_sub in ManualSubscription.query.all():
for manual_sub in ManualSubscription.all():
need_reminder = False
if arrow.now().shift(days=14) > manual_sub.end_at > arrow.now().shift(days=13):
need_reminder = True
@ -172,7 +172,7 @@ def notify_manual_sub_end():
)
extend_subscription_url = URL + "/dashboard/coinbase_checkout"
for coinbase_subscription in CoinbaseSubscription.query.all():
for coinbase_subscription in CoinbaseSubscription.all():
need_reminder = False
if (
arrow.now().shift(days=14)
@ -211,7 +211,7 @@ def notify_manual_sub_end():
def poll_apple_subscription():
"""Poll Apple API to update AppleSubscription"""
# todo: only near the end of the subscription
for apple_sub in AppleSubscription.query.all():
for apple_sub in AppleSubscription.all():
user = apple_sub.user
verify_receipt(apple_sub.receipt_data, user, APPLE_API_SECRET)
verify_receipt(apple_sub.receipt_data, user, MACAPP_APPLE_API_SECRET)
@ -224,49 +224,49 @@ def compute_metric2() -> Metric2:
_24h_ago = now.shift(days=-1)
nb_referred_user_paid = 0
for user in User.query.filter(User.referral_id.isnot(None)):
for user in User.filter(User.referral_id.isnot(None)):
if user.is_paid():
nb_referred_user_paid += 1
return Metric2.create(
date=now,
# user stats
nb_user=User.query.count(),
nb_activated_user=User.query.filter_by(activated=True).count(),
nb_user=User.count(),
nb_activated_user=User.filter_by(activated=True).count(),
# subscription stats
nb_premium=Subscription.query.filter(Subscription.cancelled.is_(False)).count(),
nb_cancelled_premium=Subscription.query.filter(
nb_premium=Subscription.filter(Subscription.cancelled.is_(False)).count(),
nb_cancelled_premium=Subscription.filter(
Subscription.cancelled.is_(True)
).count(),
# todo: filter by expires_date > now
nb_apple_premium=AppleSubscription.query.count(),
nb_manual_premium=ManualSubscription.query.filter(
nb_apple_premium=AppleSubscription.count(),
nb_manual_premium=ManualSubscription.filter(
ManualSubscription.end_at > now,
ManualSubscription.is_giveaway.is_(False),
).count(),
nb_coinbase_premium=CoinbaseSubscription.query.filter(
nb_coinbase_premium=CoinbaseSubscription.filter(
CoinbaseSubscription.end_at > now
).count(),
# referral stats
nb_referred_user=User.query.filter(User.referral_id.isnot(None)).count(),
nb_referred_user=User.filter(User.referral_id.isnot(None)).count(),
nb_referred_user_paid=nb_referred_user_paid,
nb_alias=Alias.query.count(),
nb_alias=Alias.count(),
# email log stats
nb_forward_last_24h=EmailLog.query.filter(EmailLog.created_at > _24h_ago)
nb_forward_last_24h=EmailLog.filter(EmailLog.created_at > _24h_ago)
.filter_by(bounced=False, is_spam=False, is_reply=False, blocked=False)
.count(),
nb_bounced_last_24h=EmailLog.query.filter(EmailLog.created_at > _24h_ago)
nb_bounced_last_24h=EmailLog.filter(EmailLog.created_at > _24h_ago)
.filter_by(bounced=True)
.count(),
nb_reply_last_24h=EmailLog.query.filter(EmailLog.created_at > _24h_ago)
nb_reply_last_24h=EmailLog.filter(EmailLog.created_at > _24h_ago)
.filter_by(is_reply=True)
.count(),
nb_block_last_24h=EmailLog.query.filter(EmailLog.created_at > _24h_ago)
nb_block_last_24h=EmailLog.filter(EmailLog.created_at > _24h_ago)
.filter_by(blocked=True)
.count(),
# other stats
nb_verified_custom_domain=CustomDomain.query.filter_by(verified=True).count(),
nb_app=Client.query.count(),
nb_verified_custom_domain=CustomDomain.filter_by(verified=True).count(),
nb_app=Client.count(),
commit=True,
)
@ -309,7 +309,7 @@ def bounce_report() -> List[Tuple[str, int]]:
"""
min_dt = arrow.now().shift(days=-1)
query = (
db.session.query(User.email, func.count(EmailLog.id).label("count"))
Session.query(User.email, func.count(EmailLog.id).label("count"))
.join(EmailLog, EmailLog.user_id == User.id)
.filter(EmailLog.bounced, EmailLog.created_at > min_dt)
.group_by(User.email)
@ -354,7 +354,7 @@ def alias_creation_report() -> List[Tuple[str, int]]:
"""
min_dt = arrow.now().shift(days=-7)
query = (
db.session.query(
Session.query(
User.email,
func.count(Alias.id).label("count"),
func.date(Alias.created_at).label("date"),
@ -381,7 +381,7 @@ def stats():
stats_today = compute_metric2()
stats_yesterday = (
Metric2.query.filter(Metric2.date < stats_today.date)
Metric2.filter(Metric2.date < stats_today.date)
.order_by(Metric2.date.desc())
.first()
)
@ -442,13 +442,13 @@ nb_referred_user_upgrade: {stats_today.nb_referred_user_paid} - {increase_percen
def migrate_domain_trash():
"""Move aliases from global trash to domain trash if applicable"""
for deleted_alias in DeletedAlias.query.all():
for deleted_alias in DeletedAlias.all():
alias_domain = get_email_domain_part(deleted_alias.email)
if not SLDomain.get_by(domain=alias_domain):
custom_domain = CustomDomain.get_by(domain=alias_domain)
if custom_domain:
LOG.e("move %s to domain %s trash", deleted_alias, custom_domain)
db.session.add(
Session.add(
DomainDeletedAlias(
user_id=custom_domain.user_id,
email=deleted_alias.email,
@ -458,13 +458,13 @@ def migrate_domain_trash():
)
DeletedAlias.delete(deleted_alias.id)
db.session.commit()
Session.commit()
def set_custom_domain_for_alias():
"""Go through all aliases and make sure custom_domain is correctly set"""
sl_domains = [sl_domain.domain for sl_domain in SLDomain.query.all()]
for alias in Alias.query.filter(Alias.custom_domain_id.is_(None)):
sl_domains = [sl_domain.domain for sl_domain in SLDomain.all()]
for alias in Alias.filter(Alias.custom_domain_id.is_(None)):
if (
not any(alias.email.endswith(f"@{sl_domain}") for sl_domain in sl_domains)
and not alias.custom_domain_id
@ -477,7 +477,7 @@ def set_custom_domain_for_alias():
else: # phantom domain
LOG.d("phantom domain %s %s %s", alias.user, alias, alias.enabled)
db.session.commit()
Session.commit()
def sanity_check():
@ -487,7 +487,7 @@ def sanity_check():
- detect if there's mailbox that's using a invalid domain
"""
mailbox_ids = (
db.session.query(Mailbox.id)
Session.query(Mailbox.id)
.filter(Mailbox.verified.is_(True), Mailbox.disabled.is_(False))
.all()
)
@ -544,23 +544,23 @@ def sanity_check():
else: # reset nb check
mailbox.nb_failed_checks = 0
db.session.commit()
Session.commit()
for user in User.filter_by(activated=True).all():
if sanitize_email(user.email) != user.email:
LOG.e("%s does not have sanitized email", user)
for alias in Alias.query.all():
for alias in Alias.all():
if sanitize_email(alias.email) != alias.email:
LOG.e("Alias %s email not sanitized", alias)
if alias.name and "\n" in alias.name:
alias.name = alias.name.replace("\n", "")
db.session.commit()
Session.commit()
LOG.e("Alias %s name contains linebreak %s", alias, alias.name)
contact_email_sanity_date = arrow.get("2021-01-12")
for contact in Contact.query.all():
for contact in Contact.all():
if sanitize_email(contact.reply_email) != contact.reply_email:
LOG.e("Contact %s reply-email not sanitized", contact)
@ -573,13 +573,13 @@ def sanity_check():
if not contact.invalid_email and not is_valid_email(contact.website_email):
LOG.e("%s invalid email", contact)
contact.invalid_email = True
db.session.commit()
Session.commit()
for mailbox in Mailbox.query.all():
for mailbox in Mailbox.all():
if sanitize_email(mailbox.email) != mailbox.email:
LOG.e("Mailbox %s address not sanitized", mailbox)
for contact in Contact.query.all():
for contact in Contact.all():
if normalize_reply_email(contact.reply_email) != contact.reply_email:
LOG.e(
"Contact %s reply email is not normalized %s",
@ -587,7 +587,7 @@ def sanity_check():
contact.reply_email,
)
for domain in CustomDomain.query.all():
for domain in CustomDomain.all():
if domain.name and "\n" in domain.name:
LOG.e("Domain %s name contain linebreak %s", domain, domain.name)
@ -600,9 +600,7 @@ def sanity_check():
def check_custom_domain():
LOG.d("Check verified domain for DNS issues")
for custom_domain in CustomDomain.query.filter_by(
verified=True
): # type: CustomDomain
for custom_domain in CustomDomain.filter_by(verified=True): # type: CustomDomain
mx_domains = get_mx_domains(custom_domain.domain)
if sorted(mx_domains) != sorted(EMAIL_SERVERS_WITH_PRIORITY):
@ -644,7 +642,7 @@ def check_custom_domain():
# reset checks
custom_domain.nb_failed_checks = 0
db.session.commit()
Session.commit()
def delete_old_monitoring():
@ -652,8 +650,8 @@ def delete_old_monitoring():
Delete old monitoring records
"""
max_time = arrow.now().shift(days=-30)
nb_row = Monitoring.query.filter(Monitoring.created_at < max_time).delete()
db.session.commit()
nb_row = Monitoring.filter(Monitoring.created_at < max_time).delete()
Session.commit()
LOG.d("delete monitoring records older than %s, nb row %s", max_time, nb_row)
@ -713,8 +711,8 @@ async def _hibp_check(api_key, queue):
return
alias.hibp_last_check = arrow.utcnow()
db.session.add(alias)
db.session.commit()
Session.add(alias)
Session.commit()
LOG.d("Updated breaches info for %s", alias)
@ -738,14 +736,14 @@ async def check_hibp():
hibp_entry.date = arrow.get(entry["BreachDate"])
hibp_entry.description = entry["Description"]
db.session.commit()
Session.commit()
LOG.d("Updated list of known breaches")
LOG.d("Preparing list of aliases to check")
queue = asyncio.Queue()
max_date = arrow.now().shift(days=-HIBP_SCAN_INTERVAL_DAYS)
for alias in (
Alias.query.filter(
Alias.filter(
or_(Alias.hibp_last_check.is_(None), Alias.hibp_last_check < max_date)
)
.filter(Alias.enabled)
@ -782,19 +780,19 @@ def notify_hibp():
"""
# to get a list of users that have at least a breached alias
alias_query = (
db.session.query(Alias)
Session.query(Alias)
.options(joinedload(Alias.hibp_breaches))
.filter(Alias.hibp_breaches.any())
.filter(Alias.id.notin_(db.session.query(HibpNotifiedAlias.alias_id)))
.filter(Alias.id.notin_(Session.query(HibpNotifiedAlias.alias_id)))
.distinct(Alias.user_id)
.all()
)
user_ids = [alias.user_id for alias in alias_query]
for user in User.query.filter(User.id.in_(user_ids)):
for user in User.filter(User.id.in_(user_ids)):
breached_aliases = (
db.session.query(Alias)
Session.query(Alias)
.options(joinedload(Alias.hibp_breaches))
.filter(Alias.hibp_breaches.any(), Alias.user_id == user.id)
.all()
@ -824,7 +822,7 @@ def notify_hibp():
# add the breached aliases to HibpNotifiedAlias to avoid sending another email
for alias in breached_aliases:
HibpNotifiedAlias.create(user_id=user.id, alias_id=alias.id)
db.session.commit()
Session.commit()
if __name__ == "__main__":

View File

@ -85,6 +85,7 @@ from app.config import (
ALERT_YAHOO_COMPLAINT,
TEMP_DIR,
)
from app.db import Session
from app.email import status, headers
from app.email.rate_limit import rate_limited
from app.email.spam import get_spam_score
@ -123,7 +124,6 @@ from app.email_utils import (
parse_full_address,
get_orig_message_from_yahoo_complaint,
)
from app.extensions import db
from app.log import LOG, set_message_id
from app.models import (
Alias,
@ -141,7 +141,6 @@ from app.utils import sanitize_email
from init_app import load_pgp_public_keys
from server import create_app, create_light_app
newrelic_app = None
if NEWRELIC_CONFIG_PATH:
newrelic.agent.initialize(NEWRELIC_CONFIG_PATH)
@ -157,10 +156,7 @@ def new_app():
@app.teardown_appcontext
def shutdown_session(response_or_exc):
# same as shutdown_session() in flask-sqlalchemy but this is not enough
db.session.remove()
# dispose the engine too
db.engine.dispose()
Session.remove()
return app
@ -210,7 +206,7 @@ def get_or_create_contact(from_header: str, mail_from: str, alias: Alias) -> Con
contact_name,
)
contact.name = contact_name
db.session.commit()
Session.commit()
# contact created in the past does not have mail_from and from_header field
if not contact.mail_from and mail_from:
@ -221,7 +217,7 @@ def get_or_create_contact(from_header: str, mail_from: str, alias: Alias) -> Con
mail_from,
)
contact.mail_from = mail_from
db.session.commit()
Session.commit()
else:
LOG.d(
"create contact %s for alias %s",
@ -244,10 +240,10 @@ def get_or_create_contact(from_header: str, mail_from: str, alias: Alias) -> Con
LOG.d("Create a contact with invalid email for %s", alias)
contact.invalid_email = True
db.session.commit()
Session.commit()
except IntegrityError:
LOG.w("Contact %s %s already exist", alias, contact_email)
db.session.rollback()
Session.rollback()
contact = Contact.get_by(alias_id=alias.id, website_email=contact_email)
return contact
@ -291,10 +287,10 @@ def get_or_create_reply_to_contact(
name=contact_name,
reply_email=generate_reply_email(contact_address, alias.user),
)
db.session.commit()
Session.commit()
except IntegrityError:
LOG.w("Contact %s %s already exist", alias, contact_address)
db.session.rollback()
Session.rollback()
contact = Contact.get_by(alias_id=alias.id, website_email=contact_address)
return contact
@ -341,7 +337,7 @@ def replace_header_when_forward(msg: Message, alias: Alias, header: str):
full_address.display_name,
)
contact.name = full_address.display_name
db.session.commit()
Session.commit()
else:
LOG.d(
"create contact for alias %s and email %s, header %s",
@ -359,10 +355,10 @@ def replace_header_when_forward(msg: Message, alias: Alias, header: str):
reply_email=generate_reply_email(contact_email, alias.user),
is_cc=header.lower() == "cc",
)
db.session.commit()
Session.commit()
except IntegrityError:
LOG.w("Contact %s %s already exist", alias, contact_email)
db.session.rollback()
Session.rollback()
contact = Contact.get_by(alias_id=alias.id, website_email=contact_email)
new_addrs.append(contact.new_addr())
@ -501,7 +497,7 @@ def handle_email_sent_to_ourself(alias, mailbox, msg: Message, user):
refused_email = RefusedEmail.create(
path=None, full_report_path=full_report_path, user_id=alias.user_id
)
db.session.commit()
Session.commit()
LOG.d("Create refused email %s", refused_email)
# link available for 6 days as it gets deleted in 7 days
refused_email_url = refused_email.get_url(expires_in=518400)
@ -588,7 +584,7 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str
alias_id=contact.alias_id,
commit=True,
)
db.session.commit()
Session.commit()
# do not return 5** to allow user to receive emails later when alias is enabled
return [(True, status.E200)]
@ -695,7 +691,7 @@ def forward_email_to_mailbox(
spam_report,
)
email_log.spam_score = spam_score
db.session.commit()
Session.commit()
if (user.max_spam_score and spam_score > user.max_spam_score) or (
not user.max_spam_score and spam_score > MAX_SPAM_SCORE
@ -716,7 +712,7 @@ def forward_email_to_mailbox(
)
email_log.is_spam = True
email_log.spam_status = spam_status
db.session.commit()
Session.commit()
handle_spam(contact, alias, msg, user, mailbox, email_log)
return False, status.E519
@ -846,7 +842,7 @@ def forward_email_to_mailbox(
else:
return False, status.E521
else:
db.session.commit()
Session.commit()
return True, status.E200
@ -963,7 +959,7 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str):
email_log.is_spam = True
email_log.spam_status = spam_status
db.session.commit()
Session.commit()
handle_spam(contact, alias, msg, user, mailbox, email_log, is_reply=True)
return False, status.E506
@ -1003,11 +999,11 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str):
)
# to not save the email_log
EmailLog.delete(email_log.id)
db.session.commit()
Session.commit()
# return 421 so the client can retry later
return False, status.E402
db.session.commit()
Session.commit()
# make the email comes from alias
from_header = alias.email
@ -1065,7 +1061,7 @@ def handle_reply(envelope, msg: Message, rcpt_to: str) -> (bool, str):
)
except Exception:
# to not save the email_log
db.session.rollback()
Session.rollback()
LOG.w("Cannot send email from %s to %s", alias, contact)
send_email(
@ -1218,13 +1214,13 @@ def handle_bounce_forward_phase(msg: Message, email_log: EmailLog):
refused_email = RefusedEmail.create(
path=file_path, full_report_path=full_report_path, user_id=user.id
)
db.session.flush()
Session.flush()
LOG.d("Create refused email %s", refused_email)
email_log.bounced = True
email_log.refused_email_id = refused_email.id
email_log.bounced_mailbox_id = mailbox.id
db.session.commit()
Session.commit()
refused_email_url = f"{URL}/dashboard/refused_email?highlight_id={email_log.id}"
@ -1268,7 +1264,7 @@ def handle_bounce_forward_phase(msg: Message, email_log: EmailLog):
alias,
)
alias.enabled = False
db.session.commit()
Session.commit()
send_email_with_rate_control(
user,
@ -1411,7 +1407,7 @@ def handle_bounce_reply_phase(envelope, msg: Message, email_log: EmailLog):
email_log.bounced_mailbox_id = mailbox.id
db.session.commit()
Session.commit()
refused_email_url = f"{URL}/dashboard/refused_email?highlight_id={email_log.id}"
@ -1469,10 +1465,10 @@ def handle_spam(
refused_email = RefusedEmail.create(
path=file_path, full_report_path=full_report_path, user_id=user.id
)
db.session.flush()
Session.flush()
email_log.refused_email_id = refused_email.id
db.session.commit()
Session.commit()
LOG.d("Create spam email %s", refused_email)
@ -1574,7 +1570,7 @@ def handle_unsubscribe(envelope: Envelope, msg: Message) -> str:
# Sender is owner of this alias
alias.enabled = False
db.session.commit()
Session.commit()
user = alias.user
enable_alias_url = URL + f"/dashboard/?highlight_alias_id={alias.id}"
@ -1611,7 +1607,7 @@ def handle_unsubscribe_user(user_id: int, mail_from: str) -> str:
return status.E511
user.notification = False
db.session.commit()
Session.commit()
send_email(
user.email,
@ -1676,7 +1672,7 @@ def handle_bounce(envelope, email_log: EmailLog, msg: Message) -> str:
alias = contact.alias
email_log.auto_replied = True
db.session.commit()
Session.commit()
# replace the BOUNCE_EMAIL by alias in To field
add_or_replace_header(msg, "To", alias.email)
@ -1871,8 +1867,8 @@ def handle(envelope: Envelope) -> str:
"total number email log on %s, %s is %s, %s",
alias,
alias.user,
EmailLog.query.filter(EmailLog.alias_id == alias.id).count(),
EmailLog.query.filter(EmailLog.user_id == alias.user_id).count(),
EmailLog.filter(EmailLog.alias_id == alias.id).count(),
EmailLog.filter(EmailLog.user_id == alias.user_id).count(),
)
if should_ignore_bounce(envelope.mail_from):

View File

@ -1,15 +1,14 @@
"""Initial loading script"""
from app.config import ALIAS_DOMAINS, PREMIUM_ALIAS_DOMAINS
from app.models import Mailbox, Contact, SLDomain
from app.db import Session
from app.log import LOG
from app.extensions import db
from app.models import Mailbox, Contact, SLDomain
from app.pgp_utils import load_public_key
from server import create_app
def load_pgp_public_keys():
"""Load PGP public key to keyring"""
for mailbox in Mailbox.query.filter(Mailbox.pgp_public_key.isnot(None)).all():
for mailbox in Mailbox.filter(Mailbox.pgp_public_key.isnot(None)).all():
LOG.d("Load PGP key for mailbox %s", mailbox)
fingerprint = load_public_key(mailbox.pgp_public_key)
@ -17,9 +16,9 @@ def load_pgp_public_keys():
if fingerprint != mailbox.pgp_finger_print:
LOG.e("fingerprint %s different for mailbox %s", fingerprint, mailbox)
mailbox.pgp_finger_print = fingerprint
db.session.commit()
Session.commit()
for contact in Contact.query.filter(Contact.pgp_public_key.isnot(None)).all():
for contact in Contact.filter(Contact.pgp_public_key.isnot(None)).all():
LOG.d("Load PGP key for %s", contact)
fingerprint = load_public_key(contact.pgp_public_key)
@ -28,7 +27,7 @@ def load_pgp_public_keys():
LOG.e("fingerprint %s different for contact %s", fingerprint, contact)
contact.pgp_finger_print = fingerprint
db.session.commit()
Session.commit()
LOG.d("Finish load_pgp_public_keys")
@ -48,7 +47,7 @@ def add_sl_domains():
LOG.i("Add %s to SL domain", premium_domain)
SLDomain.create(domain=premium_domain, premium_only=True)
db.session.commit()
Session.commit()
if __name__ == "__main__":

View File

@ -13,11 +13,11 @@ from app.config import (
JOB_BATCH_IMPORT,
JOB_DELETE_ACCOUNT,
)
from app.db import Session
from app.email_utils import (
send_email,
render,
)
from app.extensions import db
from app.import_utils import handle_batch_import
from app.log import LOG
from app.models import User, Job, BatchImport
@ -32,10 +32,7 @@ def new_app():
@app.teardown_appcontext
def shutdown_session(response_or_exc):
# same as shutdown_session() in flask-sqlalchemy but this is not enough
db.session.remove()
# dispose the engine too
db.engine.dispose()
Session.remove()
return app
@ -109,14 +106,14 @@ if __name__ == "__main__":
app = new_app()
with app.app_context():
for job in Job.query.filter(
for job in Job.filter(
Job.taken.is_(False), Job.run_at > min_dt, Job.run_at <= max_dt
).all():
LOG.d("Take job %s", job)
# mark the job as taken, whether it will be executed successfully or not
job.taken = True
db.session.commit()
Session.commit()
if job.name == JOB_ONBOARDING_1:
user_id = job.payload.get("user_id")
@ -161,7 +158,7 @@ if __name__ == "__main__":
user_email = user.email
LOG.w("Delete user %s", user)
User.delete(user.id)
db.session.commit()
Session.commit()
send_email(
user_email,

View File

@ -2,7 +2,7 @@ import os
from time import sleep
from app.config import HOST
from app.extensions import db
from app.db import Session
from app.log import LOG
from app.models import Monitoring
from server import create_app
@ -32,7 +32,7 @@ def get_stats():
active_queue=active_queue,
deferred_queue=deferred_queue,
)
db.session.commit()
Session.commit()
global _nb_failed
# alert when too many emails in incoming + active queue

View File

@ -71,10 +71,11 @@ from app.config import (
ROOT_DIR,
)
from app.dashboard.base import dashboard_bp
from app.db import Session
from app.developer.base import developer_bp
from app.discover.base import discover_bp
from app.email_utils import send_email, render
from app.extensions import db, login_manager, migrate, limiter
from app.extensions import login_manager, migrate, limiter
from app.jose_utils import get_jwk_key
from app.log import LOG
from app.models import (
@ -129,8 +130,6 @@ def create_light_app() -> Flask:
app.config["SQLALCHEMY_DATABASE_URI"] = DB_URI
app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
db.init_app(app)
return app
@ -200,6 +199,10 @@ def create_app() -> Flask:
session.permanent = True
app.permanent_session_lifetime = timedelta(days=7)
@app.teardown_appcontext
def cleanup(resp_or_exc):
Session.remove()
return app
@ -219,7 +222,7 @@ def fake_data():
fido_uuid=None,
)
user.trial_end = None
db.session.commit()
Session.commit()
# add a profile picture
file_path = "profile_pic.svg"
@ -230,11 +233,11 @@ def fake_data():
)
file = File.create(user_id=user.id, path=file_path, commit=True)
user.profile_picture_id = file.id
db.session.commit()
Session.commit()
# create a bounced email
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
bounce_email_file_path = "bounce.eml"
s3.upload_email_from_bytesio(
@ -298,7 +301,7 @@ def fake_data():
pgp_public_key=pgp_public_key,
)
m1.pgp_finger_print = load_public_key(pgp_public_key)
db.session.commit()
Session.commit()
# example@example.com is in a LOT of data breaches
Alias.create(email="example@example.com", user_id=user.id, mailbox_id=m1.id)
@ -314,14 +317,14 @@ def fake_data():
user_id=user.id,
mailbox_id=user.default_mailbox_id,
)
db.session.commit()
Session.commit()
if i % 5 == 0:
if i % 2 == 0:
AliasMailbox.create(alias_id=a.id, mailbox_id=user.default_mailbox_id)
else:
AliasMailbox.create(alias_id=a.id, mailbox_id=m1.id)
db.session.commit()
Session.commit()
# some aliases don't have any activity
# if i % 3 != 0:
@ -331,18 +334,18 @@ def fake_data():
# website_email=f"contact{i}@example.com",
# reply_email=f"rep{i}@sl.local",
# )
# db.session.commit()
# Session.commit()
# for _ in range(3):
# EmailLog.create(user_id=user.id, contact_id=contact.id, alias_id=contact.alias_id)
# db.session.commit()
# Session.commit()
# have some disabled alias
if i % 5 == 0:
a.enabled = False
db.session.commit()
Session.commit()
custom_domain1 = CustomDomain.create(user_id=user.id, domain="ab.cd", verified=True)
db.session.commit()
Session.commit()
Alias.create(
user_id=user.id,
@ -362,13 +365,13 @@ def fake_data():
Directory.create(user_id=user.id, name="abcd")
Directory.create(user_id=user.id, name="xyzt")
db.session.commit()
Session.commit()
# Create a client
client1 = Client.create_new(name="Demo", user_id=user.id)
client1.oauth_client_id = "client-id"
client1.oauth_client_secret = "client-secret"
db.session.commit()
Session.commit()
RedirectUri.create(
client_id=client1.id, uri="https://your-website.com/oauth-callback"
@ -377,7 +380,7 @@ def fake_data():
client2 = Client.create_new(name="Demo 2", user_id=user.id)
client2.oauth_client_id = "client-id2"
client2.oauth_client_secret = "client-secret2"
db.session.commit()
Session.commit()
ClientUser.create(user_id=user.id, client_id=client1.id, name="Fake Name")
@ -392,11 +395,11 @@ def fake_data():
number_upgraded_account=200,
payment_method="PayPal",
)
db.session.commit()
Session.commit()
for i in range(6):
Notification.create(user_id=user.id, message=f"""Hey hey <b>{i}</b> """ * 10)
db.session.commit()
Session.commit()
user2 = User.create(
email="winston@continental.com",
@ -405,7 +408,7 @@ def fake_data():
referral_id=referral.id,
)
Mailbox.create(user_id=user2.id, email="winston2@high.table", verified=True)
db.session.commit()
Session.commit()
ManualSubscription.create(
user_id=user2.id,
@ -695,7 +698,7 @@ def setup_paddle_callback(app: Flask):
LOG.d("User %s upgrades!", user)
db.session.commit()
Session.commit()
elif request.form.get("alert_name") == "subscription_payment_succeeded":
subscription_id = request.form.get("subscription_id")
@ -710,7 +713,7 @@ def setup_paddle_callback(app: Flask):
request.form.get("next_bill_date"), "YYYY-MM-DD"
).date()
db.session.commit()
Session.commit()
elif request.form.get("alert_name") == "subscription_cancelled":
subscription_id = request.form.get("subscription_id")
@ -728,7 +731,7 @@ def setup_paddle_callback(app: Flask):
sub.event_time = arrow.now()
sub.cancelled = True
db.session.commit()
Session.commit()
user = sub.user
@ -774,7 +777,7 @@ def setup_paddle_callback(app: Flask):
# make sure to set the new plan as not-cancelled
sub.cancelled = False
db.session.commit()
Session.commit()
else:
return "No such subscription", 400
return "OK"
@ -847,7 +850,7 @@ def handle_coinbase_event(event) -> bool:
else: # already expired subscription
coinbase_subscription.end_at = arrow.now().shift(years=1)
db.session.commit()
Session.commit()
send_email(
user.email,
@ -867,7 +870,6 @@ def handle_coinbase_event(event) -> bool:
def init_extensions(app: Flask):
login_manager.init_app(app)
db.init_app(app)
migrate.init_app(app)
@ -875,17 +877,17 @@ def init_admin(app):
admin = Admin(name="SimpleLogin", template_mode="bootstrap4")
admin.init_app(app, index_view=SLAdminIndexView())
admin.add_view(UserAdmin(User, db.session))
admin.add_view(AliasAdmin(Alias, db.session))
admin.add_view(MailboxAdmin(Mailbox, db.session))
admin.add_view(EmailLogAdmin(EmailLog, db.session))
admin.add_view(LifetimeCouponAdmin(LifetimeCoupon, db.session))
admin.add_view(CouponAdmin(Coupon, db.session))
admin.add_view(ManualSubscriptionAdmin(ManualSubscription, db.session))
admin.add_view(ClientAdmin(Client, db.session))
admin.add_view(CustomDomainAdmin(CustomDomain, db.session))
admin.add_view(ReferralAdmin(Referral, db.session))
admin.add_view(PayoutAdmin(Payout, db.session))
admin.add_view(UserAdmin(User, Session))
admin.add_view(AliasAdmin(Alias, Session))
admin.add_view(MailboxAdmin(Mailbox, Session))
admin.add_view(EmailLogAdmin(EmailLog, Session))
admin.add_view(LifetimeCouponAdmin(LifetimeCoupon, Session))
admin.add_view(CouponAdmin(Coupon, Session))
admin.add_view(ManualSubscriptionAdmin(ManualSubscription, Session))
admin.add_view(ClientAdmin(Client, Session))
admin.add_view(CustomDomainAdmin(CustomDomain, Session))
admin.add_view(ReferralAdmin(Referral, Session))
admin.add_view(PayoutAdmin(Payout, Session))
def register_custom_commands(app):
@ -900,12 +902,12 @@ def register_custom_commands(app):
def fill_up_email_log_alias():
"""Fill up email_log.alias_id column"""
# split all emails logs into 1000-size trunks
nb_email_log = EmailLog.query.count()
nb_email_log = EmailLog.count()
LOG.d("total trunks %s", nb_email_log // 1000 + 2)
for trunk in reversed(range(1, nb_email_log // 1000 + 2)):
nb_update = 0
for email_log, contact in (
db.session.query(EmailLog, Contact)
Session.query(EmailLog, Contact)
.filter(EmailLog.contact_id == Contact.id)
.filter(EmailLog.id <= trunk * 1000)
.filter(EmailLog.id > (trunk - 1) * 1000)
@ -915,7 +917,7 @@ def register_custom_commands(app):
nb_update += 1
LOG.d("finish trunk %s, update %s email logs", trunk, nb_update)
db.session.commit()
Session.commit()
@app.cli.command("dummy-data")
def dummy_data():

View File

@ -1,3 +1,5 @@
from app.db import Session
from app.db import Session
from time import sleep
import flask_migrate
@ -34,7 +36,7 @@ def create_db():
def change_password(user_id, new_password):
user = User.get(user_id)
user.set_password(new_password)
db.session.commit()
Session.commit()
def reset_db():
@ -44,7 +46,7 @@ def reset_db():
def send_mailbox_newsletter():
for user in User.query.order_by(User.id).all():
for user in User.order_by(User.id).all():
if user.notification and user.activated:
try:
LOG.d("Send newsletter to %s", user)
@ -60,7 +62,7 @@ def send_mailbox_newsletter():
def send_pgp_newsletter():
for user in User.query.order_by(User.id).all():
for user in User.order_by(User.id).all():
if user.notification and user.activated:
try:
LOG.d("Send PGP newsletter to %s", user)
@ -77,7 +79,7 @@ def send_pgp_newsletter():
def send_mobile_newsletter():
count = 0
for user in User.query.order_by(User.id).all():
for user in User.order_by(User.id).all():
if user.notification and user.activated:
count += 1
try:
@ -104,7 +106,7 @@ def disable_mailbox(mailbox_id):
for alias in mailbox.aliases:
alias.enabled = False
db.session.commit()
Session.commit()
email_msg = f"""Hi,

View File

@ -1,8 +1,8 @@
from flask import url_for
from app.config import PAGE_LIMIT
from app.db import Session
from app.email_utils import is_reply_email
from app.extensions import db
from app.models import User, ApiKey, Alias, Contact, EmailLog, Mailbox
from tests.utils import login
@ -18,7 +18,7 @@ def test_get_aliases_error_without_pagination(flask_client):
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.get_aliases"), headers={"Authentication": api_key.code}
@ -39,12 +39,12 @@ def test_get_aliases_with_pagination(flask_client):
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# create more aliases than PAGE_LIMIT
for _ in range(PAGE_LIMIT + 1):
Alias.create_new_random(user)
db.session.commit()
Session.commit()
# get aliases on the 1st page, should return PAGE_LIMIT aliases
r = flask_client.get(
@ -79,16 +79,16 @@ def test_get_aliases_query(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# create more aliases than PAGE_LIMIT
Alias.create_new(user, "prefix1")
Alias.create_new(user, "prefix2")
db.session.commit()
Session.commit()
# get aliases without query, should return 3 aliases as one alias is created when user is created
r = flask_client.get(
@ -111,15 +111,15 @@ def test_get_aliases_v2(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
a0 = Alias.create_new(user, "prefix0")
a1 = Alias.create_new(user, "prefix1")
db.session.commit()
Session.commit()
# << Aliases have no activity >>
r = flask_client.get(
@ -154,13 +154,13 @@ def test_get_aliases_v2(flask_client):
website_email="c0@example.com",
reply_email="re0@SL",
)
db.session.commit()
Session.commit()
EmailLog.create(
contact_id=c0.id,
user_id=user.id,
alias_id=c0.alias_id,
)
db.session.commit()
Session.commit()
# a1 has more recent activity
c1 = Contact.create(
@ -169,13 +169,13 @@ def test_get_aliases_v2(flask_client):
website_email="c1@example.com",
reply_email="re1@SL",
)
db.session.commit()
Session.commit()
EmailLog.create(
contact_id=c1.id,
user_id=user.id,
alias_id=c1.alias_id,
)
db.session.commit()
Session.commit()
# get aliases v2
r = flask_client.get(
@ -199,14 +199,14 @@ def test_delete_alias(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.delete(
url_for("api.delete_alias", alias_id=alias.id),
@ -221,14 +221,14 @@ def test_toggle_alias(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.toggle_alias", alias_id=alias.id),
@ -243,14 +243,14 @@ def test_alias_activities(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
# create some alias log
contact = Contact.create(
@ -259,7 +259,7 @@ def test_alias_activities(flask_client):
alias_id=alias.id,
user_id=alias.user_id,
)
db.session.commit()
Session.commit()
for _ in range(int(PAGE_LIMIT / 2)):
EmailLog.create(
@ -304,14 +304,14 @@ def test_update_alias(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.put(
url_for("api.update_alias", alias_id=alias.id),
@ -326,16 +326,16 @@ def test_update_alias_mailbox(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
mb = Mailbox.create(user_id=user.id, email="ab@cd.com", verified=True)
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.put(
url_for("api.update_alias", alias_id=alias.id),
@ -358,14 +358,14 @@ def test_update_alias_name(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.put(
url_for("api.update_alias", alias_id=alias.id),
@ -391,17 +391,17 @@ def test_update_alias_mailboxes(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
mb1 = Mailbox.create(user_id=user.id, email="ab1@cd.com", verified=True)
mb2 = Mailbox.create(user_id=user.id, email="ab2@cd.com", verified=True)
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.put(
url_for("api.update_alias", alias_id=alias.id),
@ -428,14 +428,14 @@ def test_update_disable_pgp(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
assert not alias.disable_pgp
r = flask_client.put(
@ -468,14 +468,14 @@ def test_alias_contacts(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
# create some alias log
for i in range(PAGE_LIMIT + 1):
@ -485,7 +485,7 @@ def test_alias_contacts(flask_client):
alias_id=alias.id,
user_id=alias.user_id,
)
db.session.commit()
Session.commit()
EmailLog.create(
contact_id=contact.id,
@ -493,7 +493,7 @@ def test_alias_contacts(flask_client):
user_id=contact.user_id,
alias_id=contact.alias_id,
)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.get_alias_contacts_route", alias_id=alias.id, page_id=0),
@ -523,14 +523,14 @@ def test_create_contact_route(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.create_contact_route", alias_id=alias.id),
@ -560,7 +560,7 @@ def test_create_contact_route(flask_client):
def test_create_contact_route_empty_contact_address(flask_client):
login(flask_client)
alias = Alias.query.first()
alias = Alias.first()
r = flask_client.post(
url_for("api.create_contact_route", alias_id=alias.id),
@ -573,7 +573,7 @@ def test_create_contact_route_empty_contact_address(flask_client):
def test_create_contact_route_invalid_contact_email(flask_client):
login(flask_client)
alias = Alias.query.first()
alias = Alias.first()
r = flask_client.post(
url_for("api.create_contact_route", alias_id=alias.id),
@ -588,14 +588,14 @@ def test_delete_contact(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
contact = Contact.create(
alias_id=alias.id,
@ -603,7 +603,7 @@ def test_delete_contact(flask_client):
reply_email="reply+random@sl.io",
user_id=alias.user_id,
)
db.session.commit()
Session.commit()
r = flask_client.delete(
url_for("api.delete_contact", contact_id=contact.id),
@ -618,15 +618,15 @@ def test_get_alias(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# create more aliases than PAGE_LIMIT
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
# get aliases on the 1st page, should return PAGE_LIMIT aliases
r = flask_client.get(

View File

@ -2,7 +2,7 @@ import json
from flask import url_for
from app.extensions import db
from app.db import Session
from app.models import User, ApiKey, AliasUsedOn, Alias
@ -10,11 +10,11 @@ def test_different_scenarios_v4(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# <<< without hostname >>>
r = flask_client.get(
@ -37,11 +37,11 @@ def test_different_scenarios_v4(flask_client):
# <<< with recommendation >>>
alias = Alias.create_new(user, prefix="test")
db.session.commit()
Session.commit()
AliasUsedOn.create(
alias_id=alias.id, hostname="www.test.com", user_id=alias.user_id
)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.options_v4", hostname="www.test.com"),
@ -55,11 +55,11 @@ def test_different_scenarios_v4_2(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# <<< without hostname >>>
r = flask_client.get(
@ -85,11 +85,11 @@ def test_different_scenarios_v4_2(flask_client):
# <<< with recommendation >>>
alias = Alias.create_new(user, prefix="test")
db.session.commit()
Session.commit()
AliasUsedOn.create(
alias_id=alias.id, hostname="www.test.com", user_id=alias.user_id
)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.options_v4", hostname="www.test.com"),
@ -103,11 +103,11 @@ def test_different_scenarios_v5(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# <<< without hostname >>>
r = flask_client.get(
@ -138,11 +138,11 @@ def test_different_scenarios_v5(flask_client):
# <<< with recommendation >>>
alias = Alias.create_new(user, prefix="test")
db.session.commit()
Session.commit()
AliasUsedOn.create(
alias_id=alias.id, hostname="www.test.com", user_id=alias.user_id
)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.options_v4", hostname="www.test.com"),

File diff suppressed because one or more lines are too long

View File

@ -1,9 +1,8 @@
import unicodedata
import pytest
import unicodedata
from flask import url_for
from app.extensions import db
from app.db import Session
from app.models import User, AccountActivation
PASSWORD_1 = "Aurélie"
@ -20,7 +19,7 @@ def test_auth_login_success(flask_client, mfa: bool):
activated=True,
enable_otp=mfa,
)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.auth_login"),
@ -49,7 +48,7 @@ def test_auth_login_device_exist(flask_client):
User.create(
email="abcd@gmail.com", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.auth_login"),
@ -138,7 +137,7 @@ def test_auth_activate_user_already_activated(flask_client):
User.create(
email="abcd@gmail.com", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.auth_activate"), json={"email": "abcd@gmail.com", "code": "123456"}
@ -214,7 +213,7 @@ def test_auth_activate_too_many_wrong_code(flask_client):
def test_auth_reactivate_success(flask_client):
User.create(email="abcd@gmail.com", password="password", name="Test User")
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.auth_reactivate"), json={"email": "abcd@gmail.com"}
@ -232,7 +231,7 @@ def test_auth_login_forgot_password(flask_client):
User.create(
email="abcd@gmail.com", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.forgot_password"),

View File

@ -3,7 +3,7 @@ from flask import url_for
from itsdangerous import Signer
from app.config import FLASK_SECRET
from app.extensions import db
from app.db import Session
from app.models import User
@ -16,7 +16,7 @@ def test_auth_mfa_success(flask_client):
enable_otp=True,
otp_secret="base32secret3232",
)
db.session.commit()
Session.commit()
totp = pyotp.TOTP(user.otp_secret)
s = Signer(FLASK_SECRET)
@ -42,7 +42,7 @@ def test_auth_wrong_mfa_key(flask_client):
enable_otp=True,
otp_secret="base32secret3232",
)
db.session.commit()
Session.commit()
totp = pyotp.TOTP(user.otp_secret)

View File

@ -1,7 +1,8 @@
from flask import url_for
from app import alias_utils
from app.extensions import db
from app.db import Session
from app.import_utils import import_from_csv
from app.models import (
User,
CustomDomain,
@ -11,7 +12,6 @@ from app.models import (
BatchImport,
File,
)
from app.import_utils import import_from_csv
from tests.utils import login
@ -21,14 +21,14 @@ def test_export(flask_client):
user2 = User.create(
email="x@y.z", password="password", name="Wrong user", activated=True
)
db.session.commit()
Session.commit()
# Remove onboarding aliases
for alias in Alias.filter_by(user_id=user1.id).all():
alias_utils.delete_alias(alias, user1)
for alias in Alias.filter_by(user_id=user2.id).all():
alias_utils.delete_alias(alias, user2)
db.session.commit()
Session.commit()
# Create domains
CustomDomain.create(
@ -37,7 +37,7 @@ def test_export(flask_client):
CustomDomain.create(
user_id=user2.id, domain="bad-destionation-domain.com", verified=True
)
db.session.commit()
Session.commit()
# Create mailboxes
mailbox1 = Mailbox.create(
@ -51,7 +51,7 @@ def test_export(flask_client):
email="baddestination@bad-destination-domain.com",
verified=True,
)
db.session.commit()
Session.commit()
# Create aliases
Alias.create(
@ -72,14 +72,14 @@ def test_export(flask_client):
note="Should not appear",
mailbox_id=badmailbox1.id,
)
db.session.commit()
Session.commit()
# Add second mailbox to an alias
AliasMailbox.create(
alias_id=alias2.id,
mailbox_id=mailbox2.id,
)
db.session.commit()
Session.commit()
# Export
r = flask_client.get(url_for("api.export_aliases"))
@ -128,7 +128,7 @@ def test_import_no_mailboxes(flask_client):
CustomDomain.create(
user_id=user.id, domain="my-domain.com", ownership_verified=True
)
db.session.commit()
Session.commit()
alias_data = [
"alias,note",
@ -180,7 +180,7 @@ def test_import(flask_client):
CustomDomain.create(
user_id=user.id, domain="my-destination-domain.com", ownership_verified=True
)
db.session.commit()
Session.commit()
# Create mailboxes
mailbox1 = Mailbox.create(
@ -189,7 +189,7 @@ def test_import(flask_client):
mailbox2 = Mailbox.create(
user_id=user.id, email="destination2@my-destination-domain.com", verified=True
)
db.session.commit()
Session.commit()
alias_data = [
"alias,note,mailboxes",

View File

@ -1,6 +1,6 @@
from flask import url_for
from app.extensions import db
from app.db import Session
from app.models import Mailbox
from tests.utils import login
@ -34,7 +34,7 @@ def test_create_mailbox(flask_client):
def test_create_mailbox_fail_for_free_user(flask_client):
user = login(flask_client)
user.trial_end = None
db.session.commit()
Session.commit()
r = flask_client.post(
"/api/mailboxes",
@ -50,7 +50,7 @@ def test_delete_mailbox(flask_client):
# create a mailbox
mb = Mailbox.create(user_id=user.id, email="mb@gmail.com")
db.session.commit()
Session.commit()
r = flask_client.delete(
f"/api/mailboxes/{mb.id}",
@ -88,7 +88,7 @@ def test_set_mailbox_as_default(flask_client):
# <<< Cannot set an unverified mailbox as default >>>
mb.verified = False
db.session.commit()
Session.commit()
r = flask_client.put(
f"/api/mailboxes/{mb.id}",
@ -104,7 +104,7 @@ def test_update_mailbox_email(flask_client):
# create a mailbox
mb = Mailbox.create(user_id=user.id, email="mb@gmail.com")
db.session.commit()
Session.commit()
r = flask_client.put(
f"/api/mailboxes/{mb.id}",
@ -122,7 +122,7 @@ def test_cancel_mailbox_email_change(flask_client):
# create a mailbox
mb = Mailbox.create(user_id=user.id, email="mb@gmail.com")
db.session.commit()
Session.commit()
# update mailbox email
r = flask_client.put(
@ -150,7 +150,7 @@ def test_get_mailboxes(flask_client):
Mailbox.create(user_id=user.id, email="m1@example.com", verified=True)
Mailbox.create(user_id=user.id, email="m2@example.com", verified=False)
db.session.commit()
Session.commit()
r = flask_client.get(
"/api/mailboxes",
@ -173,7 +173,7 @@ def test_get_mailboxes_v2(flask_client):
Mailbox.create(user_id=user.id, email="m1@example.com", verified=True)
Mailbox.create(user_id=user.id, email="m2@example.com", verified=False)
db.session.commit()
Session.commit()
r = flask_client.get(
"/api/v2/mailboxes",

View File

@ -3,7 +3,7 @@ from flask import g
from app.alias_utils import delete_alias
from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN
from app.dashboard.views.custom_alias import signer
from app.extensions import db
from app.db import Session
from app.models import Alias, CustomDomain, Mailbox, AliasUsedOn
from app.utils import random_word
from tests.utils import login
@ -86,13 +86,13 @@ def test_full_payload(flask_client):
# create another mailbox
mb = Mailbox.create(user_id=user.id, email="abcd@gmail.com", verified=True)
db.session.commit()
Session.commit()
word = random_word()
suffix = f".{word}@{EMAIL_DOMAIN}"
signed_suffix = signer.sign(suffix).decode()
assert AliasUsedOn.query.count() == 0
assert AliasUsedOn.count() == 0
r = flask_client.post(
"/api/v3/alias/custom/new?hostname=example.com",
@ -146,7 +146,7 @@ def test_custom_domain_alias(flask_client):
def test_out_of_quota(flask_client):
user = login(flask_client)
user.trial_end = None
db.session.commit()
Session.commit()
# create MAX_NB_EMAIL_FREE_PLAN custom alias to run out of quota
for _ in range(MAX_NB_EMAIL_FREE_PLAN):

View File

@ -3,7 +3,7 @@ import uuid
from flask import url_for, g
from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN
from app.extensions import db
from app.db import Session
from app.models import Alias
from tests.utils import login
@ -60,7 +60,7 @@ def test_custom_mode(flask_client):
def test_out_of_quota(flask_client):
user = login(flask_client)
user.trial_end = None
db.session.commit()
Session.commit()
# create MAX_NB_EMAIL_FREE_PLAN random alias to run out of quota
for _ in range(MAX_NB_EMAIL_FREE_PLAN):

View File

@ -1,6 +1,6 @@
from flask import url_for
from app.extensions import db
from app.db import Session
from app.models import User, ApiKey, Notification
@ -8,16 +8,16 @@ def test_get_notifications(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
# create some notifications
Notification.create(user_id=user.id, message="Test message 1")
Notification.create(user_id=user.id, message="Test message 2")
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.get_notifications", page=0),
@ -46,14 +46,14 @@ def test_mark_notification_as_read(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
Notification.create(id=1, user_id=user.id, message="Test message 1")
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("api.mark_as_read", notification_id=1),

View File

@ -1,6 +1,6 @@
from app.api.serializer import get_alias_infos_with_pagination_v3
from app.config import PAGE_LIMIT
from app.extensions import db
from app.db import Session
from app.models import User, Alias, Mailbox, Contact
from tests.utils import create_user
@ -75,7 +75,7 @@ def test_get_alias_infos_with_pagination_v3_query_alias_mailboxes(flask_client):
alias = Alias.first()
mb = Mailbox.create(user_id=user.id, email="mb@gmail.com")
alias._mailboxes.append(mb)
db.session.commit()
Session.commit()
alias_infos = get_alias_infos_with_pagination_v3(user, mailbox_id=mb.id)
assert len(alias_infos) == 1
@ -96,7 +96,7 @@ def test_get_alias_infos_with_pagination_v3_query_alias_note(flask_client):
alias = Alias.first()
alias.note = "test note"
db.session.commit()
Session.commit()
alias_infos = get_alias_infos_with_pagination_v3(user, query="test note")
assert len(alias_infos) == 1
@ -114,7 +114,7 @@ def test_get_alias_infos_with_pagination_v3_query_alias_name(flask_client):
alias = Alias.first()
alias.name = "Test Name"
db.session.commit()
Session.commit()
alias_infos = get_alias_infos_with_pagination_v3(user, query="test name")
assert len(alias_infos) == 1
@ -135,7 +135,7 @@ def test_get_alias_infos_with_pagination_v3_no_duplicate(flask_client):
alias = Alias.first()
mb = Mailbox.create(user_id=user.id, email="mb@gmail.com")
alias._mailboxes.append(mb)
db.session.commit()
Session.commit()
alias_infos = get_alias_infos_with_pagination_v3(user)
assert len(alias_infos) == 1
@ -182,7 +182,7 @@ def test_get_alias_infos_pinned_alias(flask_client):
for i in range(2 * PAGE_LIMIT):
Alias.create_new_random(user)
first_alias = Alias.query.order_by(Alias.id).first()
first_alias = Alias.order_by(Alias.id).first()
# should return PAGE_LIMIT alias
alias_infos = get_alias_infos_with_pagination_v3(user)
@ -192,7 +192,7 @@ def test_get_alias_infos_pinned_alias(flask_client):
# pin the first alias
first_alias.pinned = True
db.session.commit()
Session.commit()
alias_infos = get_alias_infos_with_pagination_v3(user)
# now first_alias is the first result

View File

@ -1,6 +1,6 @@
from flask import url_for
from app.extensions import db
from app.db import Session
from app.models import User, ApiKey
from tests.utils import login
@ -9,11 +9,11 @@ def test_user_in_trial(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# create api_key
api_key = ApiKey.create(user.id, "for test")
db.session.commit()
Session.commit()
r = flask_client.get(
url_for("api.user_info"), headers={"Authentication": api_key.code}
@ -42,7 +42,7 @@ def test_wrong_api_key(flask_client):
def test_create_api_key(flask_client):
# create user, user is activated
User.create(email="a@b.c", password="password", name="Test User", activated=True)
db.session.commit()
Session.commit()
# login user
flask_client.post(
@ -61,7 +61,7 @@ def test_create_api_key(flask_client):
def test_logout(flask_client):
# create user, user is activated
User.create(email="a@b.c", password="password", name="Test User", activated=True)
db.session.commit()
Session.commit()
# login user
flask_client.post(

View File

@ -1,6 +1,6 @@
from flask import url_for
from app.extensions import db
from app.db import Session
from app.models import User
@ -9,7 +9,7 @@ def test_unactivated_user_login(flask_client):
# create user, user is not activated
User.create(email="a@b.c", password="password", name="Test User")
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("auth.login"),
@ -29,7 +29,7 @@ def test_activated_user_login(flask_client):
# create user, user is activated
User.create(email="a@b.c", password="password", name="Test User", activated=True)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("auth.login"),

View File

@ -2,18 +2,20 @@ import os
# use the tests/test.env config fle
# flake8: noqa: E402
import sqlalchemy
os.environ["CONFIG"] = os.path.abspath(
os.path.join(os.path.dirname(os.path.dirname(__file__)), "tests/test.env")
)
import sqlalchemy
from app.db import Session, engine, connection
from app.models import Base
from psycopg2 import errors
from psycopg2.errorcodes import DEPENDENT_OBJECTS_STILL_EXIST
import pytest
from app.extensions import db
from server import create_app
from init_app import add_sl_domains
@ -24,7 +26,7 @@ app.config["SERVER_NAME"] = "sl.test"
with app.app_context():
# enable pg_trgm extension
with db.engine.connect() as conn:
with engine.connect() as conn:
try:
conn.execute("DROP EXTENSION if exists pg_trgm")
conn.execute("CREATE EXTENSION pg_trgm")
@ -33,7 +35,7 @@ with app.app_context():
print(">>> pg_trgm can't be dropped, ignore")
conn.execute("Rollback")
db.create_all()
Base.metadata.create_all(engine)
add_sl_domains()
@ -45,20 +47,14 @@ def flask_app():
@pytest.fixture
def flask_client():
with app.app_context():
# replace db.session to that we can rollback all commits that can be made during a test
# inspired from http://alexmic.net/flask-sqlalchemy-pytest/
connection = db.engine.connect()
transaction = connection.begin()
options = dict(bind=connection, binds={})
session = db.create_scoped_session(options=options)
db.session = session
transaction = connection.begin()
with app.app_context():
try:
client = app.test_client()
yield client
finally:
# roll back all commits made during a test
transaction.rollback()
connection.close()
session.remove()
Session.rollback()
Session.close()

View File

@ -11,7 +11,7 @@ def test_add_contact_success(flask_client):
login(flask_client)
alias = Alias.first()
assert Contact.query.count() == 0
assert Contact.count() == 0
# <<< Create a new contact >>>
flask_client.post(
@ -23,7 +23,7 @@ def test_add_contact_success(flask_client):
follow_redirects=True,
)
# a new contact is added
assert Contact.query.count() == 1
assert Contact.count() == 1
contact = Contact.first()
assert contact.website_email == "abcd@gmail.com"
@ -37,8 +37,8 @@ def test_add_contact_success(flask_client):
follow_redirects=True,
)
# a new contact is added
assert Contact.query.count() == 2
contact = Contact.query.filter(Contact.id != contact.id).first()
assert Contact.count() == 2
contact = Contact.filter(Contact.id != contact.id).first()
assert contact.website_email == "another@gmail.com"
assert contact.name == "First Last"
@ -53,5 +53,5 @@ def test_add_contact_success(flask_client):
)
# no new contact is added
assert Contact.query.count() == 2
assert Contact.count() == 2
assert "Invalid email format. Email must be either email@example.com" in str(r.data)

View File

@ -1,5 +1,5 @@
from app.dashboard.views import alias_transfer
from app.extensions import db
from app.db import Session
from app.models import (
Alias,
Mailbox,
@ -14,7 +14,7 @@ def test_alias_transfer(flask_client):
mb = Mailbox.create(user_id=user.id, email="mb@gmail.com", commit=True)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
AliasMailbox.create(alias_id=alias.id, mailbox_id=mb.id, commit=True)

View File

@ -8,7 +8,7 @@ from app.dashboard.views.custom_alias import (
get_available_suffixes,
AliasSuffix,
)
from app.extensions import db
from app.db import Session
from app.models import (
Mailbox,
CustomDomain,
@ -46,13 +46,13 @@ def test_add_alias_success(flask_client):
assert r.status_code == 200
assert f"Alias prefix.12345@{EMAIL_DOMAIN} has been created" in str(r.data)
alias = Alias.query.order_by(Alias.created_at.desc()).first()
alias = Alias.order_by(Alias.created_at.desc()).first()
assert not alias._mailboxes
def test_add_alias_multiple_mailboxes(flask_client):
user = login(flask_client)
db.session.commit()
Session.commit()
alias_suffix = AliasSuffix(
is_custom=False,
@ -64,7 +64,7 @@ def test_add_alias_multiple_mailboxes(flask_client):
# create with a multiple mailboxes
mb1 = Mailbox.create(user_id=user.id, email="m1@example.com", verified=True)
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("dashboard.custom_alias"),
@ -78,18 +78,18 @@ def test_add_alias_multiple_mailboxes(flask_client):
assert r.status_code == 200
assert f"Alias prefix.12345@{EMAIL_DOMAIN} has been created" in str(r.data)
alias = Alias.query.order_by(Alias.created_at.desc()).first()
alias = Alias.order_by(Alias.created_at.desc()).first()
assert alias._mailboxes
def test_not_show_unverified_mailbox(flask_client):
"""make sure user unverified mailbox is not shown to user"""
user = login(flask_client)
db.session.commit()
Session.commit()
Mailbox.create(user_id=user.id, email="m1@example.com", verified=True)
Mailbox.create(user_id=user.id, email="m2@example.com", verified=False)
db.session.commit()
Session.commit()
r = flask_client.get(url_for("dashboard.custom_alias"))
@ -99,7 +99,7 @@ def test_not_show_unverified_mailbox(flask_client):
def test_verify_prefix_suffix(flask_client):
user = login(flask_client)
db.session.commit()
Session.commit()
CustomDomain.create(user_id=user.id, domain="test.com", verified=True)
@ -128,7 +128,7 @@ def test_available_suffixes(flask_client):
def test_available_suffixes_default_domain(flask_client):
user = login(flask_client)
sl_domain = SLDomain.query.first()
sl_domain = SLDomain.first()
CustomDomain.create(user_id=user.id, domain="test.com", verified=True, commit=True)
user.default_alias_public_domain_id = sl_domain.id
@ -166,7 +166,7 @@ def test_available_suffixes_random_prefix_generation(flask_client):
def test_add_already_existed_alias(flask_client):
user = login(flask_client)
db.session.commit()
Session.commit()
another_user = User.create(
email="a2@b.c",
@ -208,7 +208,7 @@ def test_add_already_existed_alias(flask_client):
def test_add_alias_in_global_trash(flask_client):
user = login(flask_client)
db.session.commit()
Session.commit()
another_user = User.create(
email="a2@b.c",
@ -233,9 +233,9 @@ def test_add_alias_in_global_trash(flask_client):
commit=True,
)
assert DeletedAlias.query.count() == 0
assert DeletedAlias.count() == 0
delete_alias(alias, another_user)
assert DeletedAlias.query.count() == 1
assert DeletedAlias.count() == 1
# create the same alias, should return error
r = flask_client.post(
@ -267,9 +267,9 @@ def test_add_alias_in_custom_domain_trash(flask_client):
commit=True,
)
assert DomainDeletedAlias.query.count() == 0
assert DomainDeletedAlias.count() == 0
delete_alias(alias, user)
assert DomainDeletedAlias.query.count() == 1
assert DomainDeletedAlias.count() == 1
# create the same alias, should return error
suffix = "@ab.cd"

View File

@ -1,13 +1,13 @@
from flask import url_for
from app.extensions import db
from app.db import Session
from tests.utils import login
def test_add_domain_success(flask_client):
user = login(flask_client)
user.lifetime = True
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("dashboard.custom_domain"),
@ -23,7 +23,7 @@ def test_add_domain_same_as_user_email(flask_client):
"""cannot add domain if user personal email uses this domain"""
user = login(flask_client)
user.lifetime = True
db.session.commit()
Session.commit()
r = flask_client.post(
url_for("dashboard.custom_domain"),

View File

@ -8,7 +8,7 @@ from tests.utils import login
def test_create_random_alias_success(flask_client):
login(flask_client)
assert Alias.query.count() == 1
assert Alias.count() == 1
r = flask_client.post(
url_for("dashboard.index"),
@ -16,7 +16,7 @@ def test_create_random_alias_success(flask_client):
follow_redirects=True,
)
assert r.status_code == 200
assert Alias.query.count() == 2
assert Alias.count() == 2
def test_too_many_requests(flask_client):

View File

@ -2,7 +2,7 @@ from app.config import (
MAX_ACTIVITY_DURING_MINUTE_PER_ALIAS,
MAX_ACTIVITY_DURING_MINUTE_PER_MAILBOX,
)
from app.extensions import db
from app.db import Session
from app.email.rate_limit import (
rate_limited_forward_phase,
rate_limited_for_alias,
@ -16,11 +16,11 @@ def test_rate_limited_forward_phase_for_alias(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
# no rate limiting for a new alias
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
assert not rate_limited_for_alias(alias)
# rate limit when there's a previous activity on alias
@ -30,14 +30,14 @@ def test_rate_limited_forward_phase_for_alias(flask_client):
website_email="contact@example.com",
reply_email="rep@sl.local",
)
db.session.commit()
Session.commit()
for _ in range(MAX_ACTIVITY_DURING_MINUTE_PER_ALIAS + 1):
EmailLog.create(
user_id=user.id,
contact_id=contact.id,
alias_id=contact.alias_id,
)
db.session.commit()
Session.commit()
assert rate_limited_for_alias(alias)
@ -46,10 +46,10 @@ def test_rate_limited_forward_phase_for_mailbox(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
contact = Contact.create(
user_id=user.id,
@ -57,14 +57,14 @@ def test_rate_limited_forward_phase_for_mailbox(flask_client):
website_email="contact@example.com",
reply_email="rep@sl.local",
)
db.session.commit()
Session.commit()
for _ in range(MAX_ACTIVITY_DURING_MINUTE_PER_MAILBOX + 1):
EmailLog.create(
user_id=user.id,
contact_id=contact.id,
alias_id=contact.alias_id,
)
db.session.commit()
Session.commit()
EmailLog.create(
user_id=user.id,
@ -75,7 +75,7 @@ def test_rate_limited_forward_phase_for_mailbox(flask_client):
# Create another alias with the same mailbox
# will be rate limited as there's a previous activity on mailbox
alias2 = Alias.create_new_random(user)
db.session.commit()
Session.commit()
assert rate_limited_for_mailbox(alias2)
@ -91,10 +91,10 @@ def test_rate_limited_reply_phase(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
contact = Contact.create(
user_id=user.id,
@ -102,13 +102,13 @@ def test_rate_limited_reply_phase(flask_client):
website_email="contact@example.com",
reply_email="rep@sl.local",
)
db.session.commit()
Session.commit()
for _ in range(MAX_ACTIVITY_DURING_MINUTE_PER_ALIAS + 1):
EmailLog.create(
user_id=user.id,
contact_id=contact.id,
alias_id=contact.alias_id,
)
db.session.commit()
Session.commit()
assert rate_limited_reply_phase("rep@sl.local")

View File

@ -4,7 +4,7 @@ from urllib.parse import urlparse, parse_qs
from flask import url_for
from app.extensions import db
from app.db import Session
from app.jose_utils import verify_id_token, decode_id_token
from app.models import Client, User, ClientUser
from app.oauth.views.authorize import (
@ -39,10 +39,10 @@ def test_construct_url():
def test_authorize_page_non_login_user(flask_client):
"""make sure to display login page for non-authenticated user"""
user = User.create("test@test.com", "test user")
db.session.commit()
Session.commit()
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for(
@ -63,7 +63,7 @@ def test_authorize_page_login_user_non_supported_flow(flask_client):
"""return 400 if the flow is not supported"""
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# Not provide any flow
r = flask_client.get(
@ -102,7 +102,7 @@ def test_authorize_page_login_user(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for(
@ -128,7 +128,7 @@ def test_authorize_code_flow_no_openid_scope(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# user allows client on the authorization page
r = flask_client.post(
@ -217,7 +217,7 @@ def test_authorize_code_flow_with_openid_scope(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# user allows client on the authorization page
r = flask_client.post(
@ -310,7 +310,7 @@ def test_authorize_token_flow(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# user allows client on the authorization page
r = flask_client.post(
@ -357,7 +357,7 @@ def test_authorize_id_token_flow(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# user allows client on the authorization page
r = flask_client.post(
@ -406,7 +406,7 @@ def test_authorize_token_id_token_flow(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# user allows client on the authorization page
r = flask_client.post(
@ -496,7 +496,7 @@ def test_authorize_code_id_token_flow(flask_client):
user = login(flask_client)
client = Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
# user allows client on the authorization page
r = flask_client.post(
@ -629,7 +629,7 @@ def test_authorize_page_invalid_client_id(flask_client):
user = login(flask_client)
Client.create_new("test client", user.id)
db.session.commit()
Session.commit()
r = flask_client.get(
url_for(
@ -654,7 +654,7 @@ def test_authorize_page_http_not_allowed(flask_client):
client = Client.create_new("test client", user.id)
client.approved = True
db.session.commit()
Session.commit()
r = flask_client.get(
url_for(
@ -676,7 +676,7 @@ def test_authorize_page_unknown_redirect_uri(flask_client):
client = Client.create_new("test client", user.id)
client.approved = True
db.session.commit()
Session.commit()
r = flask_client.get(
url_for(

View File

@ -1,5 +1,5 @@
from app.alias_utils import delete_alias, check_alias_prefix
from app.extensions import db
from app.db import Session
from app.models import User, Alias, DeletedAlias
@ -41,8 +41,8 @@ def test_delete_alias_already_in_trash(flask_client):
)
# add the alias to global trash
db.session.add(DeletedAlias(email=alias.email))
db.session.commit()
Session.add(DeletedAlias(email=alias.email))
Session.commit()
delete_alias(alias, user)
assert Alias.get_by(email="first@d1.test") is None

View File

@ -1,4 +1,5 @@
import pytest
from app.config import sl_getenv

View File

@ -4,6 +4,7 @@ from email.message import EmailMessage
import arrow
from app.config import MAX_ALERT_24H, EMAIL_DOMAIN, BOUNCE_EMAIL
from app.db import Session
from app.email_utils import (
get_email_domain_part,
can_create_directory_for_address,
@ -31,7 +32,6 @@ from app.email_utils import (
get_header_unicode,
parse_full_address,
)
from app.extensions import db
from app.models import User, CustomDomain, Alias, Contact, EmailLog, IgnoreBounceSender
# flake8: noqa: E101, W191
@ -136,7 +136,7 @@ def test_send_email_with_rate_control(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
for _ in range(MAX_ALERT_24H):
assert send_email_with_rate_control(
@ -598,7 +598,7 @@ def test_should_disable(flask_client):
include_sender_in_reverse_alias=True,
)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
assert not should_disable(alias)
@ -623,7 +623,7 @@ def test_should_disable(flask_client):
# should not affect another alias
alias2 = Alias.create_new_random(user)
db.session.commit()
Session.commit()
assert not should_disable(alias2)
@ -631,7 +631,7 @@ def test_should_disable_bounces_every_day(flask_client):
"""if an alias has bounces every day at least 9 days in the last 10 days, disable alias"""
user = login(flask_client)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
assert not should_disable(alias)
@ -661,7 +661,7 @@ def test_should_disable_bounces_account(flask_client):
user = login(flask_client)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
# create a lot of bounces on alias
contact = Contact.create(
@ -690,7 +690,7 @@ def test_should_disable_bounces_account(flask_client):
def test_should_disable_bounce_consecutive_days(flask_client):
user = login(flask_client)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
contact = Contact.create(
user_id=user.id,

View File

@ -1,4 +1,4 @@
from app.extensions import db
from app.db import Session
from app.jose_utils import make_id_token, verify_id_token
from app.models import ClientUser, User, Client
@ -7,15 +7,15 @@ def test_encode_decode(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True
)
db.session.commit()
Session.commit()
client1 = Client.create_new(name="Demo", user_id=user.id)
client1.oauth_client_id = "client-id"
client1.oauth_client_secret = "client-secret"
db.session.commit()
Session.commit()
client_user = ClientUser.create(client_id=client1.id, user_id=user.id)
db.session.commit()
Session.commit()
jwt_token = make_id_token(client_user)

View File

@ -3,8 +3,8 @@ from uuid import UUID
import pytest
from app.config import EMAIL_DOMAIN, MAX_NB_EMAIL_FREE_PLAN
from app.db import Session
from app.email_utils import parse_full_address
from app.extensions import db
from app.models import (
generate_email,
User,
@ -53,7 +53,7 @@ def test_suggested_emails_for_user_who_cannot_create_new_alias(flask_client):
# make sure user runs out of quota to create new email
for i in range(MAX_NB_EMAIL_FREE_PLAN):
Alias.create_new(user=user, prefix="test")
db.session.commit()
Session.commit()
suggested_email, other_emails = user.suggested_emails(website_name="test")
@ -88,7 +88,7 @@ def test_website_send_to(flask_client):
)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
# non-empty name
c1 = Contact.create(
@ -122,7 +122,7 @@ def test_new_addr(flask_client):
)
alias = Alias.create_new_random(user)
db.session.commit()
Session.commit()
# default sender_format is 'via'
c1 = Contact.create(
@ -137,18 +137,18 @@ def test_new_addr(flask_client):
# Make sure email isn't duplicated if sender name equals email
c1.name = "abcd@example.com"
db.session.commit()
Session.commit()
assert c1.new_addr() == '"abcd(a)example.com" <rep@SL>'
# set sender_format = AT
user.sender_format = SenderFormatEnum.AT.value
c1.name = "First Last"
db.session.commit()
Session.commit()
assert c1.new_addr() == '"First Last - abcd at example.com" <rep@SL>'
# unicode name
c1.name = "Nhơn Nguyễn"
db.session.commit()
Session.commit()
assert (
c1.new_addr()
== "=?utf-8?q?Nh=C6=A1n_Nguy=E1=BB=85n_-_abcd_at_example=2Ecom?= <rep@SL>"
@ -182,11 +182,11 @@ def test_mailbox_delete(flask_client):
# alias has 2 mailboxes
alias = Alias.create_new(user, "prefix", mailbox_id=m1.id)
db.session.commit()
Session.commit()
alias._mailboxes.append(m2)
alias._mailboxes.append(m3)
db.session.commit()
Session.commit()
assert len(alias.mailboxes) == 3

View File

@ -1,6 +1,6 @@
import arrow
from app.extensions import db
from app.db import Session
from app.models import User, CoinbaseSubscription
from server import handle_coinbase_event
@ -44,7 +44,7 @@ def test_handle_coinbase_event_extend_subscription(flask_client):
activated=True,
)
user.trial_end = None
db.session.commit()
Session.commit()
cb = CoinbaseSubscription.create(
user_id=user.id, end_at=arrow.now().shift(days=-400), commit=True