black new version

This commit is contained in:
Son NK 2020-08-27 10:20:48 +02:00
parent bb6e2a35ca
commit fdedc24358
23 changed files with 145 additions and 60 deletions

View File

@ -23,8 +23,7 @@ from app.models import (
def try_auto_create(address: str) -> Optional[Alias]:
"""Try to auto-create the alias using directory or catch-all domain
"""
"""Try to auto-create the alias using directory or catch-all domain"""
alias = try_auto_create_catch_all_domain(address)
if not alias:
alias = try_auto_create_directory(address)
@ -77,7 +76,8 @@ def try_auto_create_directory(address: str) -> Optional[Alias]:
db.session.flush()
for i in range(1, len(mailboxes)):
AliasMailbox.create(
alias_id=alias.id, mailbox_id=mailboxes[i].id,
alias_id=alias.id,
mailbox_id=mailboxes[i].id,
)
db.session.commit()
@ -127,7 +127,8 @@ def try_auto_create_catch_all_domain(address: str) -> Optional[Alias]:
db.session.flush()
for i in range(1, len(mailboxes)):
AliasMailbox.create(
alias_id=alias.id, mailbox_id=mailboxes[i].id,
alias_id=alias.id,
mailbox_id=mailboxes[i].id,
)
db.session.commit()
return alias

View File

@ -195,7 +195,8 @@ def get_alias_infos_with_pagination_v3(
func.sum(case([(EmailLog.is_reply, 1)], else_=0)).label("nb_reply"),
func.sum(
case(
[(and_(EmailLog.is_reply == False, EmailLog.blocked), 1)], else_=0,
[(and_(EmailLog.is_reply == False, EmailLog.blocked), 1)],
else_=0,
)
).label("nb_blocked"),
func.sum(
@ -203,7 +204,8 @@ def get_alias_infos_with_pagination_v3(
[
(
and_(
EmailLog.is_reply == False, EmailLog.blocked == False,
EmailLog.is_reply == False,
EmailLog.blocked == False,
),
1,
)

View File

@ -310,7 +310,8 @@ def verify_receipt(receipt_data, user, password) -> Optional[AppleSubscription]:
# try sandbox_url
LOG.warning("Use the sandbox url instead")
r = requests.post(
_SANDBOX_URL, json={"receipt-data": receipt_data, "password": password},
_SANDBOX_URL,
json={"receipt-data": receipt_data, "password": password},
)
data = r.json()
@ -466,7 +467,8 @@ def verify_receipt(receipt_data, user, password) -> Optional[AppleSubscription]:
if data["status"] != 0:
LOG.warning(
"verifyReceipt status !=0, probably invalid receipt. User %s", user,
"verifyReceipt status !=0, probably invalid receipt. User %s",
user,
)
return None

View File

@ -288,7 +288,8 @@ def new_custom_alias_v3():
for i in range(1, len(mailboxes)):
AliasMailbox.create(
alias_id=alias.id, mailbox_id=mailboxes[i].id,
alias_id=alias.id,
mailbox_id=mailboxes[i].id,
)
db.session.commit()

View File

@ -54,7 +54,9 @@ def register():
# 'hostname': '127.0.0.1'}
if not hcaptcha_res["success"]:
LOG.warning(
"User put wrong captcha %s %s", form.email.data, hcaptcha_res,
"User put wrong captcha %s %s",
form.email.data,
hcaptcha_res,
)
flash("Wrong Captcha", "error")
return render_template(

View File

@ -70,7 +70,8 @@ def get_contact_infos(alias: Alias, page=0, contact_id=None) -> [ContactInfo]:
[
(
and_(
EmailLog.is_reply == False, EmailLog.blocked == False,
EmailLog.is_reply == False,
EmailLog.blocked == False,
),
1,
)
@ -80,15 +81,28 @@ def get_contact_infos(alias: Alias, page=0, contact_id=None) -> [ContactInfo]:
).label("nb_forward"),
func.max(EmailLog.created_at).label("max_email_log_created_at"),
)
.join(EmailLog, EmailLog.contact_id == Contact.id, isouter=True,)
.join(
EmailLog,
EmailLog.contact_id == Contact.id,
isouter=True,
)
.filter(Contact.alias_id == alias.id)
.group_by(Contact.id)
.subquery()
)
q = (
db.session.query(Contact, EmailLog, sub.c.nb_reply, sub.c.nb_forward,)
.join(EmailLog, EmailLog.contact_id == Contact.id, isouter=True,)
db.session.query(
Contact,
EmailLog,
sub.c.nb_reply,
sub.c.nb_forward,
)
.join(
EmailLog,
EmailLog.contact_id == Contact.id,
isouter=True,
)
.filter(Contact.alias_id == alias.id)
.filter(Contact.id == sub.c.id)
.filter(
@ -167,7 +181,10 @@ def alias_contact_manager(alias_id):
except Exception:
flash(f"{contact_addr} is invalid", "error")
return redirect(
url_for("dashboard.alias_contact_manager", alias_id=alias_id,)
url_for(
"dashboard.alias_contact_manager",
alias_id=alias_id,
)
)
contact_email = contact_email.lower()

View File

@ -143,7 +143,8 @@ def custom_alias():
for i in range(1, len(mailboxes)):
AliasMailbox.create(
alias_id=alias.id, mailbox_id=mailboxes[i].id,
alias_id=alias.id,
mailbox_id=mailboxes[i].id,
)
db.session.commit()

View File

@ -111,7 +111,8 @@ def domain_detail_dns(custom_domain_id):
custom_domain.dmarc_verified = False
db.session.commit()
flash(
f"DMARC: The TXT record is not correctly set", "warning",
f"DMARC: The TXT record is not correctly set",
"warning",
)
dmarc_ok = False
dmarc_errors = txt_records
@ -207,7 +208,8 @@ def domain_detail_trash(custom_domain_id):
DomainDeletedAlias.delete(deleted_alias.id)
db.session.commit()
flash(
f"{deleted_alias.email} can now be re-created", "success",
f"{deleted_alias.email} can now be re-created",
"success",
)
return redirect(

View File

@ -102,7 +102,10 @@ def index():
flash("Unknown error, sorry for the inconvenience", "error")
return redirect(
url_for(
"dashboard.index", query=query, sort=sort, filter=alias_filter,
"dashboard.index",
query=query,
sort=sort,
filter=alias_filter,
)
)

View File

@ -13,9 +13,13 @@ def refused_email_route():
if highlight_id:
highlight_id = int(highlight_id)
email_logs: [EmailLog] = EmailLog.query.filter(
EmailLog.user_id == current_user.id, EmailLog.refused_email_id != None
).order_by(EmailLog.id.desc()).all()
email_logs: [EmailLog] = (
EmailLog.query.filter(
EmailLog.user_id == current_user.id, EmailLog.refused_email_id != None
)
.order_by(EmailLog.id.desc())
.all()
)
# make sure the highlighted email_log is the first email_log
highlight_index = None

View File

@ -403,8 +403,7 @@ def get_mx_domain_list(domain) -> [str]:
def personal_email_already_used(email: str) -> bool:
"""test if an email can be used as user email
"""
"""test if an email can be used as user email"""
if User.get_by(email=email):
return True
@ -490,11 +489,11 @@ def get_addrs_from_header(msg: Message, header) -> [str]:
def get_spam_info(msg: Message, max_score=None) -> (bool, str):
"""parse SpamAssassin header to detect whether a message is classified as spam.
Return (is spam, spam status detail)
The header format is
```X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
DKIM_VALID_AU,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2,SPF_PASS,
URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.2```
Return (is spam, spam status detail)
The header format is
```X-Spam-Status: No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
DKIM_VALID_AU,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2,SPF_PASS,
URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.2```
"""
spamassassin_status = msg["X-Spam-Status"]
if not spamassassin_status:
@ -505,11 +504,11 @@ def get_spam_info(msg: Message, max_score=None) -> (bool, str):
def get_spam_from_header(spam_status_header, max_score=None) -> (bool, str):
"""get spam info from X-Spam-Status header
Return (is spam, spam status detail).
The spam_status_header has the following format
```No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
DKIM_VALID_AU,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2,SPF_PASS,
URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.2```
Return (is spam, spam status detail).
The spam_status_header has the following format
```No, score=-0.1 required=5.0 tests=DKIM_SIGNED,DKIM_VALID,
DKIM_VALID_AU,RCVD_IN_DNSWL_BLOCKED,RCVD_IN_MSPIKE_H2,SPF_PASS,
URIBL_BLOCKED autolearn=unavailable autolearn_force=no version=3.4.2```
"""
# yes or no
spamassassin_answer = spam_status_header[: spam_status_header.find(",")]

View File

@ -17,14 +17,19 @@ def greylisting_needed_for_alias(alias: Alias) -> bool:
nb_activity = (
db.session.query(EmailLog)
.join(Contact, EmailLog.contact_id == Contact.id)
.filter(Contact.alias_id == alias.id, EmailLog.created_at > min_time,)
.filter(
Contact.alias_id == alias.id,
EmailLog.created_at > min_time,
)
.group_by(EmailLog.id)
.count()
)
if nb_activity > MAX_ACTIVITY_DURING_MINUTE_PER_ALIAS:
LOG.d(
"Too much forward on alias %s. Nb Activity %s", alias, nb_activity,
"Too much forward on alias %s. Nb Activity %s",
alias,
nb_activity,
)
return True
@ -39,7 +44,10 @@ def greylisting_needed_for_mailbox(alias: Alias) -> bool:
db.session.query(EmailLog)
.join(Contact, EmailLog.contact_id == Contact.id)
.join(Alias, Contact.alias_id == Alias.id)
.filter(Alias.mailbox_id == alias.mailbox_id, EmailLog.created_at > min_time,)
.filter(
Alias.mailbox_id == alias.mailbox_id,
EmailLog.created_at > min_time,
)
.group_by(EmailLog.id)
.count()
)

View File

@ -608,7 +608,9 @@ class MfaBrowser(db.Model, ModelMixin):
found = True
return MfaBrowser.create(
user_id=user.id, token=token, expires=arrow.now().shift(days=30),
user_id=user.id,
token=token,
expires=arrow.now().shift(days=30),
)
@classmethod

View File

@ -34,7 +34,9 @@ def upload_from_bytesio(key: str, bs: BytesIO, content_type="string"):
else:
_session.resource("s3").Bucket(BUCKET).put_object(
Key=key, Body=bs, ContentType=content_type,
Key=key,
Body=bs,
ContentType=content_type,
)

View File

@ -151,8 +151,7 @@ class Stats:
def stats_before(moment: Arrow) -> Stats:
"""return the stats before a specific moment, ignoring all stats come from users in IGNORED_EMAILS
"""
"""return the stats before a specific moment, ignoring all stats come from users in IGNORED_EMAILS"""
# nb user
q = User.query
for ie in IGNORED_EMAILS:
@ -175,7 +174,9 @@ def stats_before(moment: Arrow) -> Stats:
q = (
db.session.query(EmailLog)
.join(User, EmailLog.user_id == User.id)
.filter(EmailLog.created_at < moment,)
.filter(
EmailLog.created_at < moment,
)
)
for ie in IGNORED_EMAILS:
q = q.filter(~User.email.contains(ie))

View File

@ -162,13 +162,18 @@ def get_or_create_contact(
if contact:
if contact.name != contact_name:
LOG.d(
"Update contact %s name %s to %s", contact, contact.name, contact_name,
"Update contact %s name %s to %s",
contact,
contact.name,
contact_name,
)
contact.name = contact_name
db.session.commit()
else:
LOG.debug(
"create contact for alias %s and contact %s", alias, contact_from_header,
"create contact for alias %s and contact %s",
alias,
contact_from_header,
)
reply_email = generate_reply_email()
@ -355,7 +360,8 @@ def prepare_pgp_message(orig_msg: Message, pgp_fingerprint: str):
# Delete unnecessary headers in orig_msg except to save space
delete_all_headers_except(
orig_msg, _MIME_HEADERS,
orig_msg,
_MIME_HEADERS,
)
first = MIMEApplication(
@ -644,7 +650,10 @@ async def handle_reply(envelope, smtp: SMTP, msg: Message, rcpt_to: str) -> (boo
# the "reply email"
if mail_from == "<>":
LOG.warning(
"Bounce when sending to alias %s from %s, user %s", alias, contact, user,
"Bounce when sending to alias %s from %s, user %s",
alias,
contact,
user,
)
handle_bounce(contact, alias, msg, user)
@ -1399,7 +1408,9 @@ class MailHandler:
return ret
except Exception:
LOG.exception(
"email handling fail %s -> %s", envelope.mail_from, envelope.rcpt_tos,
"email handling fail %s -> %s",
envelope.mail_from,
envelope.rcpt_tos,
)
return "421 SL Retry later"

View File

@ -54,7 +54,7 @@ def demo():
@app.route("/callback", methods=["GET"])
def callback():
""" Step 3: Retrieving an access token.
"""Step 3: Retrieving an access token.
The user has been redirected back from the provider to your registered
callback URL. With this redirection comes an authorization code included
in the redirect URL. We will use that to obtain an access token.
@ -75,8 +75,7 @@ def callback():
@app.route("/profile", methods=["GET"])
def profile():
"""Fetching a protected resource using an OAuth 2 token.
"""
"""Fetching a protected resource using an OAuth 2 token."""
simplelogin = OAuth2Session(client_id, token=session["oauth_token"])
return jsonify(simplelogin.get(userinfo_url).json())

View File

@ -82,7 +82,11 @@ from app.oauth.base import oauth_bp
if SENTRY_DSN:
LOG.d("enable sentry")
sentry_sdk.init(
dsn=SENTRY_DSN, integrations=[FlaskIntegration(), SqlalchemyIntegration(),],
dsn=SENTRY_DSN,
integrations=[
FlaskIntegration(),
SqlalchemyIntegration(),
],
)
# the app is served behin nginx which uses http and not https

View File

@ -243,14 +243,16 @@ def test_auth_login_forgot_password(flask_client):
db.session.commit()
r = flask_client.post(
url_for("api.forgot_password"), json={"email": "abcd@gmail.com"},
url_for("api.forgot_password"),
json={"email": "abcd@gmail.com"},
)
assert r.status_code == 200
# No such email, still return 200
r = flask_client.post(
url_for("api.forgot_password"), json={"email": "not-exist@b.c"},
url_for("api.forgot_password"),
json={"email": "not-exist@b.c"},
)
assert r.status_code == 200

View File

@ -177,7 +177,8 @@ def test_get_mailboxes(flask_client):
db.session.commit()
r = flask_client.get(
url_for("api.get_mailboxes"), headers={"Authentication": api_key.code},
url_for("api.get_mailboxes"),
headers={"Authentication": api_key.code},
)
assert r.status_code == 200
# m2@example.com is not returned as it's not verified

View File

@ -120,7 +120,11 @@ def test_success_v2(flask_client):
r = flask_client.post(
url_for("api.new_custom_alias_v2", hostname="www.test.com"),
headers={"Authentication": api_key.code},
json={"alias_prefix": "prefix", "signed_suffix": suffix, "note": "test note",},
json={
"alias_prefix": "prefix",
"signed_suffix": suffix,
"note": "test note",
},
)
assert r.status_code == 201
@ -163,7 +167,11 @@ def test_cannot_create_alias_in_trash(flask_client):
r = flask_client.post(
url_for("api.new_custom_alias_v2", hostname="www.test.com"),
headers={"Authentication": api_key.code},
json={"alias_prefix": "prefix", "signed_suffix": suffix, "note": "test note",},
json={
"alias_prefix": "prefix",
"signed_suffix": suffix,
"note": "test note",
},
)
# assert alias creation is successful
@ -179,14 +187,21 @@ def test_cannot_create_alias_in_trash(flask_client):
r = flask_client.post(
url_for("api.new_custom_alias_v2", hostname="www.test.com"),
headers={"Authentication": api_key.code},
json={"alias_prefix": "prefix", "signed_suffix": suffix, "note": "test note",},
json={
"alias_prefix": "prefix",
"signed_suffix": suffix,
"note": "test note",
},
)
assert r.status_code == 409
def test_success_v3(flask_client):
user = User.create(
email="a@b.c", password="password", name="Test User", activated=True,
email="a@b.c",
password="password",
name="Test User",
activated=True,
)
db.session.commit()

View File

@ -69,6 +69,9 @@ def test_logout(flask_client):
)
# logout
r = flask_client.get(url_for("auth.logout"), follow_redirects=True,)
r = flask_client.get(
url_for("auth.logout"),
follow_redirects=True,
)
assert r.status_code == 200

View File

@ -82,7 +82,10 @@ def test_add_or_replace_header():
def test_parseaddr_unicode():
# only email
assert parseaddr_unicode("abcd@gmail.com") == ("", "abcd@gmail.com",)
assert parseaddr_unicode("abcd@gmail.com") == (
"",
"abcd@gmail.com",
)
# ascii address
assert parseaddr_unicode("First Last <abcd@gmail.com>") == (