4.46.2
All checks were successful
Build-Release-Image / Build-Image (linux/amd64) (push) Successful in 3m7s
Build-Release-Image / Build-Image (linux/arm64) (push) Successful in 4m35s
Build-Release-Image / Merge-Images (push) Successful in 21s
Build-Release-Image / Create-Release (push) Successful in 9s
Build-Release-Image / Notify (push) Successful in 3s
All checks were successful
Build-Release-Image / Build-Image (linux/amd64) (push) Successful in 3m7s
Build-Release-Image / Build-Image (linux/arm64) (push) Successful in 4m35s
Build-Release-Image / Merge-Images (push) Successful in 21s
Build-Release-Image / Create-Release (push) Successful in 9s
Build-Release-Image / Notify (push) Successful in 3s
This commit is contained in:
parent
f51d31f431
commit
9002bbad09
@ -5,6 +5,7 @@ import arrow
|
|||||||
from flask import Blueprint, request, jsonify, g
|
from flask import Blueprint, request, jsonify, g
|
||||||
from flask_login import current_user
|
from flask_login import current_user
|
||||||
|
|
||||||
|
from app import constants
|
||||||
from app.db import Session
|
from app.db import Session
|
||||||
from app.models import ApiKey
|
from app.models import ApiKey
|
||||||
|
|
||||||
@ -18,7 +19,9 @@ def authorize_request() -> Optional[Tuple[str, int]]:
|
|||||||
api_key = ApiKey.get_by(code=api_code)
|
api_key = ApiKey.get_by(code=api_code)
|
||||||
|
|
||||||
if not api_key:
|
if not api_key:
|
||||||
if current_user.is_authenticated:
|
if current_user.is_authenticated and request.headers.get(
|
||||||
|
constants.HEADER_ALLOW_API_COOKIES
|
||||||
|
):
|
||||||
g.user = current_user
|
g.user = current_user
|
||||||
else:
|
else:
|
||||||
return jsonify(error="Wrong api key"), 401
|
return jsonify(error="Wrong api key"), 401
|
||||||
|
1
app/app/constants.py
Normal file
1
app/app/constants.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
HEADER_ALLOW_API_COOKIES = "X-Sl-Allowcookies"
|
@ -76,7 +76,6 @@ class SendRequest:
|
|||||||
file_path = os.path.join(config.SAVE_UNSENT_DIR, file_name)
|
file_path = os.path.join(config.SAVE_UNSENT_DIR, file_name)
|
||||||
self.save_request_to_file(file_path)
|
self.save_request_to_file(file_path)
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def save_request_to_failed_dir(self, prefix: str = "DeliveryRetryFail"):
|
def save_request_to_failed_dir(self, prefix: str = "DeliveryRetryFail"):
|
||||||
file_name = (
|
file_name = (
|
||||||
f"{prefix}-{int(time.time())}-{uuid.uuid4()}.{SendRequest.SAVE_EXTENSION}"
|
f"{prefix}-{int(time.time())}-{uuid.uuid4()}.{SendRequest.SAVE_EXTENSION}"
|
||||||
|
@ -87,6 +87,7 @@ class RedisSessionStore(SessionInterface):
|
|||||||
httponly = self.get_cookie_httponly(app)
|
httponly = self.get_cookie_httponly(app)
|
||||||
secure = self.get_cookie_secure(app)
|
secure = self.get_cookie_secure(app)
|
||||||
expires = self.get_expiration_time(app, session)
|
expires = self.get_expiration_time(app, session)
|
||||||
|
samesite = self.get_cookie_samesite(app)
|
||||||
val = pickle.dumps(dict(session))
|
val = pickle.dumps(dict(session))
|
||||||
ttl = int(app.permanent_session_lifetime.total_seconds())
|
ttl = int(app.permanent_session_lifetime.total_seconds())
|
||||||
# Only 5 minutes for non-authenticated sessions.
|
# Only 5 minutes for non-authenticated sessions.
|
||||||
@ -109,6 +110,7 @@ class RedisSessionStore(SessionInterface):
|
|||||||
domain=domain,
|
domain=domain,
|
||||||
path=path,
|
path=path,
|
||||||
secure=secure,
|
secure=secure,
|
||||||
|
samesite=samesite,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,13 +235,14 @@ def get_or_create_contact(from_header: str, mail_from: str, alias: Alias) -> Con
|
|||||||
contact.mail_from = mail_from
|
contact.mail_from = mail_from
|
||||||
Session.commit()
|
Session.commit()
|
||||||
else:
|
else:
|
||||||
|
alias_id = alias.id
|
||||||
try:
|
try:
|
||||||
contact_email_for_reply = (
|
contact_email_for_reply = (
|
||||||
contact_email if is_valid_email(contact_email) else ""
|
contact_email if is_valid_email(contact_email) else ""
|
||||||
)
|
)
|
||||||
contact = Contact.create(
|
contact = Contact.create(
|
||||||
user_id=alias.user_id,
|
user_id=alias.user_id,
|
||||||
alias_id=alias.id,
|
alias_id=alias_id,
|
||||||
website_email=contact_email,
|
website_email=contact_email,
|
||||||
name=contact_name,
|
name=contact_name,
|
||||||
mail_from=mail_from,
|
mail_from=mail_from,
|
||||||
@ -261,9 +262,11 @@ def get_or_create_contact(from_header: str, mail_from: str, alias: Alias) -> Con
|
|||||||
|
|
||||||
Session.commit()
|
Session.commit()
|
||||||
except IntegrityError:
|
except IntegrityError:
|
||||||
LOG.w(f"Contact with email {contact_email} for alias {alias} already exist")
|
# No need to manually rollback, as IntegrityError already rolls back
|
||||||
Session.rollback()
|
LOG.info(
|
||||||
contact = Contact.get_by(alias_id=alias.id, website_email=contact_email)
|
f"Contact with email {contact_email} for alias_id {alias_id} already existed, fetching from DB"
|
||||||
|
)
|
||||||
|
contact = Contact.get_by(alias_id=alias_id, website_email=contact_email)
|
||||||
|
|
||||||
return contact
|
return contact
|
||||||
|
|
||||||
@ -662,6 +665,9 @@ def handle_forward(envelope, msg: Message, rcpt_to: str) -> List[Tuple[bool, str
|
|||||||
from_header = get_header_unicode(msg[headers.FROM])
|
from_header = get_header_unicode(msg[headers.FROM])
|
||||||
LOG.d("Create or get contact for from_header:%s", from_header)
|
LOG.d("Create or get contact for from_header:%s", from_header)
|
||||||
contact = get_or_create_contact(from_header, envelope.mail_from, alias)
|
contact = get_or_create_contact(from_header, envelope.mail_from, alias)
|
||||||
|
alias = (
|
||||||
|
contact.alias
|
||||||
|
) # In case the Session was closed in the get_or_create we re-fetch the alias
|
||||||
|
|
||||||
reply_to_contact = None
|
reply_to_contact = None
|
||||||
if msg[headers.REPLY_TO]:
|
if msg[headers.REPLY_TO]:
|
||||||
|
@ -29,7 +29,7 @@ from sentry_sdk.integrations.flask import FlaskIntegration
|
|||||||
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
|
from sentry_sdk.integrations.sqlalchemy import SqlalchemyIntegration
|
||||||
from werkzeug.middleware.proxy_fix import ProxyFix
|
from werkzeug.middleware.proxy_fix import ProxyFix
|
||||||
|
|
||||||
from app import paddle_utils, config, paddle_callback
|
from app import paddle_utils, config, paddle_callback, constants
|
||||||
from app.admin_model import (
|
from app.admin_model import (
|
||||||
SLAdminIndexView,
|
SLAdminIndexView,
|
||||||
UserAdmin,
|
UserAdmin,
|
||||||
@ -430,6 +430,7 @@ def jinja2_filter(app):
|
|||||||
PAGE_LIMIT=PAGE_LIMIT,
|
PAGE_LIMIT=PAGE_LIMIT,
|
||||||
ZENDESK_ENABLED=ZENDESK_ENABLED,
|
ZENDESK_ENABLED=ZENDESK_ENABLED,
|
||||||
MAX_NB_EMAIL_FREE_PLAN=MAX_NB_EMAIL_FREE_PLAN,
|
MAX_NB_EMAIL_FREE_PLAN=MAX_NB_EMAIL_FREE_PLAN,
|
||||||
|
HEADER_ALLOW_API_COOKIES=constants.HEADER_ALLOW_API_COOKIES,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -702,7 +703,12 @@ def setup_coinbase_commerce(app):
|
|||||||
|
|
||||||
|
|
||||||
def handle_coinbase_event(event) -> bool:
|
def handle_coinbase_event(event) -> bool:
|
||||||
user_id = int(event["data"]["metadata"]["user_id"])
|
server_user_id = event["data"]["metadata"]["user_id"]
|
||||||
|
try:
|
||||||
|
user_id = int(server_user_id)
|
||||||
|
except ValueError:
|
||||||
|
user_id = int(float(server_user_id))
|
||||||
|
|
||||||
code = event["data"]["code"]
|
code = event["data"]["code"]
|
||||||
user = User.get(user_id)
|
user = User.get(user_id)
|
||||||
if not user:
|
if not user:
|
||||||
|
@ -264,6 +264,7 @@
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
'{{HEADER_ALLOW_API_COOKIES}}': 'allow'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -80,7 +80,10 @@
|
|||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
generateRandomAlias: async function (event) {
|
generateRandomAlias: async function (event) {
|
||||||
let result = await fetch('/api/alias/random/new', {method: 'POST'});
|
let result = await fetch('/api/alias/random/new', {method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'{{HEADER_ALLOW_API_COOKIES}}': 'allow'
|
||||||
|
}});
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
let data = await result.json();
|
let data = await result.json();
|
||||||
this.ticket_email = data.alias;
|
this.ticket_email = data.alias;
|
||||||
|
@ -216,6 +216,7 @@
|
|||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
'{{HEADER_ALLOW_API_COOKIES}}': 'allow'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -232,6 +233,7 @@
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
'{{HEADER_ALLOW_API_COOKIES}}': 'allow'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
@ -249,6 +251,7 @@
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
'{{HEADER_ALLOW_API_COOKIES}}': 'allow'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
@ -87,6 +87,7 @@
|
|||||||
method: "GET",
|
method: "GET",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
|
'{{HEADER_ALLOW_API_COOKIES}}': 'allow'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (res.ok) {
|
if (res.ok) {
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
import os
|
import os
|
||||||
|
|
||||||
|
from flask import testing
|
||||||
|
|
||||||
# use the tests/test.env config fle
|
# use the tests/test.env config fle
|
||||||
# flake8: noqa: E402
|
# flake8: noqa: E402
|
||||||
|
|
||||||
@ -42,7 +44,16 @@ def flask_app():
|
|||||||
yield app
|
yield app
|
||||||
|
|
||||||
|
|
||||||
from app import config
|
from app import config, constants
|
||||||
|
|
||||||
|
|
||||||
|
class CustomTestClient(testing.FlaskClient):
|
||||||
|
def open(self, *args, **kwargs):
|
||||||
|
if isinstance(args[0], str):
|
||||||
|
headers = kwargs.pop("headers", {})
|
||||||
|
headers.update({constants.HEADER_ALLOW_API_COOKIES: "allow"})
|
||||||
|
kwargs["headers"] = headers
|
||||||
|
return super().open(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@ -53,7 +64,9 @@ def flask_client():
|
|||||||
# disable rate limit during test
|
# disable rate limit during test
|
||||||
config.DISABLE_RATE_LIMIT = True
|
config.DISABLE_RATE_LIMIT = True
|
||||||
try:
|
try:
|
||||||
|
app.test_client_class = CustomTestClient
|
||||||
client = app.test_client()
|
client = app.test_client()
|
||||||
|
client.environ_base[constants.HEADER_ALLOW_API_COOKIES] = "allow"
|
||||||
yield client
|
yield client
|
||||||
finally:
|
finally:
|
||||||
# disable rate limit again as some tests might enable rate limit
|
# disable rate limit again as some tests might enable rate limit
|
||||||
|
Loading…
x
Reference in New Issue
Block a user