mirror of
https://github.com/pretix/pretix.git
synced 2026-11-04 00:29:52 +00:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
96a4b3d7f6 |
No files matched your search
+7
-5
@@ -34,9 +34,9 @@ dependencies = [
|
||||
"celery==5.6.*",
|
||||
"chardet==5.2.*",
|
||||
"cryptography>=50.0.2",
|
||||
"css-inline==0.23.*",
|
||||
"css-inline==0.22.*",
|
||||
"defusedcsv>=3.0.0",
|
||||
"dnspython==2.9.*",
|
||||
"dnspython==2.*",
|
||||
"Django[argon2]==5.2.*,>=5.2.17",
|
||||
"django-bootstrap3==26.3",
|
||||
"django-compressor==4.6.0",
|
||||
@@ -58,6 +58,7 @@ dependencies = [
|
||||
"django-scopes==2.1.*",
|
||||
"django-statici18n==2.8.*",
|
||||
"djangorestframework>=3.18.3",
|
||||
"dnspython==2.8.*",
|
||||
"drf_ujson2==1.7.*",
|
||||
"geoip2==5.*",
|
||||
"importlib_metadata==9.*", # Polyfill, we can probably drop this once we require Python 3.10+
|
||||
@@ -66,7 +67,8 @@ dependencies = [
|
||||
"kombu==5.6.*",
|
||||
"libsass==0.23.*",
|
||||
"lxml",
|
||||
"markdown==3.11.2",
|
||||
"markdown==3.11.2", # 3.3.5 requires importlib-metadata>=4.4, but django-bootstrap3 requires importlib-metadata<3.
|
||||
# We can upgrade markdown again once django-bootstrap3 upgrades or once we drop Python 3.6 and 3.7
|
||||
"mt-940==4.30.*",
|
||||
"oauthlib==3.3.*",
|
||||
"openpyxl==3.1.*",
|
||||
@@ -80,7 +82,7 @@ dependencies = [
|
||||
"protobuf==7.36.*",
|
||||
"psycopg2-binary",
|
||||
"pycountry",
|
||||
"pycparser==3.12",
|
||||
"pycparser==3.11",
|
||||
"pycryptodome==3.24.*",
|
||||
"pypdf==6.20.*",
|
||||
"python-bidi==0.6.*", # Support for Arabic in reportlab
|
||||
@@ -120,7 +122,7 @@ dev = [
|
||||
"pytest-cache",
|
||||
"pytest-cov",
|
||||
"pytest-django==4.*",
|
||||
"pytest-mock==3.17.*",
|
||||
"pytest-mock==3.16.*",
|
||||
"pytest-sugar",
|
||||
"pytest-xdist==3.8.*",
|
||||
"pytest-playwright",
|
||||
|
||||
@@ -131,27 +131,6 @@ class PeppolIdValidator:
|
||||
def __init__(self, validate_online=False):
|
||||
self.validate_online = validate_online
|
||||
|
||||
def _validate_peppol_id_online(self, value):
|
||||
base_hostnames = ['participant.sml.prod.tech.peppol.org', 'participant.sml.test.tech.peppol.org']
|
||||
smp_id = base64.b32encode(hashlib.sha256(value.lower().encode()).digest()).decode().rstrip("=")
|
||||
for base_hostname in base_hostnames:
|
||||
smp_domain = f'{smp_id}.iso6523-actorid-upis.{base_hostname}'
|
||||
resolver = dns.resolver.Resolver()
|
||||
try:
|
||||
answers = resolver.resolve(smp_domain, 'NAPTR', lifetime=1.0)
|
||||
if answers:
|
||||
return value
|
||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||
# ID not registered, do not set found=True
|
||||
pass
|
||||
except Exception: # noqa
|
||||
# Error likely on our end or infrastructure is down, allow user to proceed
|
||||
return value
|
||||
|
||||
raise ValidationError(
|
||||
_("The Peppol participant ID is not registered on the Peppol network."),
|
||||
)
|
||||
|
||||
def __call__(self, value):
|
||||
if ":" not in value:
|
||||
raise ValidationError(_("A Peppol participant ID always starts with a prefix, followed by a colon (:)."))
|
||||
@@ -167,7 +146,25 @@ class PeppolIdValidator:
|
||||
params={"number": prefix})
|
||||
|
||||
if self.validate_online:
|
||||
value = self._validate_peppol_id_online(value)
|
||||
base_hostnames = ['participant.sml.prod.tech.peppol.org', 'participant.sml.test.tech.peppol.org']
|
||||
smp_id = base64.b32encode(hashlib.sha256(value.lower().encode()).digest()).decode().rstrip("=")
|
||||
for base_hostname in base_hostnames:
|
||||
smp_domain = f'{smp_id}.iso6523-actorid-upis.{base_hostname}'
|
||||
resolver = dns.resolver.Resolver()
|
||||
try:
|
||||
answers = resolver.resolve(smp_domain, 'NAPTR', lifetime=1.0)
|
||||
if answers:
|
||||
return value
|
||||
except (dns.resolver.NXDOMAIN, dns.resolver.NoAnswer):
|
||||
# ID not registered, do not set found=True
|
||||
pass
|
||||
except Exception: # noqa
|
||||
# Error likely on our end or infrastructure is down, allow user to proceed
|
||||
return value
|
||||
|
||||
raise ValidationError(
|
||||
_("The Peppol participant ID is not registered on the Peppol network."),
|
||||
)
|
||||
|
||||
return value
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ class PluginSignal(Generic[T], django.dispatch.Signal):
|
||||
def _is_receiver_active(self, sender, receiver):
|
||||
return is_receiver_active(sender, receiver)
|
||||
|
||||
def send(self, sender: T, **named) -> List[Tuple[Callable, Any]]:
|
||||
def send(self, sender: T, skip_exceptions=False, **named) -> List[Tuple[Callable, Any]]:
|
||||
"""
|
||||
Send signal from sender to all connected receivers that belong to
|
||||
plugins enabled for the given event / organizer.
|
||||
@@ -164,8 +164,19 @@ class PluginSignal(Generic[T], django.dispatch.Signal):
|
||||
|
||||
for receiver in self._live_receivers(sender)[0]:
|
||||
if self._is_receiver_active(sender, receiver):
|
||||
response = receiver(signal=self, sender=sender, **named)
|
||||
responses.append((receiver, response))
|
||||
try:
|
||||
response = receiver(signal=self, sender=sender, **named)
|
||||
except Exception as e:
|
||||
if skip_exceptions:
|
||||
logger.exception("Signal handler skipped due to exception")
|
||||
if settings.SENTRY_ENABLED:
|
||||
import sentry_sdk
|
||||
|
||||
sentry_sdk.capture_exception(e)
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
responses.append((receiver, response))
|
||||
return responses
|
||||
|
||||
def send_chained(self, sender: T, chain_kwarg_name, **named) -> List[Tuple[Callable, Any]]:
|
||||
@@ -312,6 +323,30 @@ class OrganizerPluginSignal(PluginSignal[Organizer]):
|
||||
|
||||
|
||||
class GlobalSignal(django.dispatch.Signal):
|
||||
def send(self, sender, skip_exceptions=False, **named):
|
||||
if (
|
||||
not self.receivers
|
||||
or self.sender_receivers_cache.get(sender) is NO_RECEIVERS
|
||||
):
|
||||
return []
|
||||
responses = []
|
||||
sync_receivers, _ = self._live_receivers(sender)
|
||||
for receiver in sync_receivers:
|
||||
try:
|
||||
response = receiver(signal=self, sender=sender, **named)
|
||||
except Exception as e:
|
||||
if skip_exceptions:
|
||||
logger.exception("Signal handler skipped due to exception")
|
||||
if settings.SENTRY_ENABLED:
|
||||
import sentry_sdk
|
||||
|
||||
sentry_sdk.capture_exception(e)
|
||||
else:
|
||||
raise
|
||||
else:
|
||||
responses.append((receiver, response))
|
||||
return responses
|
||||
|
||||
def send_chained(self, sender: Event, chain_kwarg_name, **named) -> List[Tuple[Callable, Any]]:
|
||||
"""
|
||||
Send signal from sender to all connected receivers. The return value of the first receiver
|
||||
|
||||
@@ -135,7 +135,7 @@ def get_event_navigation(request: HttpRequest):
|
||||
# It would be better to allow plugins to handle the permission themselves, but for backwards compatibility
|
||||
# we need to have it in the "if" statement
|
||||
event_settings += sorted(
|
||||
sum((list(a[1]) for a in nav_event_settings.send(request.event, request=request)), []),
|
||||
sum((list(a[1]) for a in nav_event_settings.send(request.event, skip_exceptions=True, request=request)), []),
|
||||
key=lambda r: r['label']
|
||||
)
|
||||
if event_settings:
|
||||
@@ -487,7 +487,7 @@ def get_global_navigation(request):
|
||||
})
|
||||
|
||||
merge_in(nav, sorted(
|
||||
sum((list(a[1]) for a in nav_global.send(request, request=request)), []),
|
||||
sum((list(a[1]) for a in nav_global.send(request, skip_exceptions=True, request=request)), []),
|
||||
key=lambda r: (1 if r.get('parent') else 0, r['label'])
|
||||
))
|
||||
return nav
|
||||
@@ -718,7 +718,7 @@ def get_organizer_navigation(request):
|
||||
})
|
||||
|
||||
merge_in(nav, sorted(
|
||||
sum((list(a[1]) for a in nav_organizer.send(request.organizer, request=request, organizer=request.organizer)),
|
||||
sum((list(a[1]) for a in nav_organizer.send(request.organizer, skip_exceptions=True, request=request, organizer=request.organizer)),
|
||||
[]),
|
||||
key=lambda r: (1 if r.get('parent') else 0, r['label'])
|
||||
))
|
||||
|
||||
@@ -225,14 +225,6 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3" id="editor-toolbox-area">
|
||||
{% if checkin_qr_warning %}
|
||||
<div class="alert alert-warning" id="nocheckin-notice">
|
||||
{% blocktrans trimmed %}
|
||||
This layout does not contain a QR code that can be used to scan tickets at check-in.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="panel panel-default" id="toolbox">
|
||||
<div class="panel-heading">
|
||||
<span id="toolbox-heading">
|
||||
|
||||
@@ -370,7 +370,7 @@ def event_index(request, organizer, event):
|
||||
|
||||
widgets = []
|
||||
if can_view_orders:
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event, subevent=subevent, lazy=True):
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event, subevent=subevent, lazy=True, skip_exceptions=True):
|
||||
widgets.extend(result)
|
||||
|
||||
ctx = {
|
||||
@@ -406,7 +406,7 @@ def event_index_widgets_lazy(request, organizer, event):
|
||||
request=request)
|
||||
widgets = []
|
||||
if can_view_orders:
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event, subevent=subevent, lazy=False):
|
||||
for r, result in event_dashboard_widgets.send(sender=request.event, subevent=subevent, lazy=False, skip_exceptions=True):
|
||||
widgets.extend(result)
|
||||
|
||||
return build_json_response(widgets)
|
||||
@@ -666,7 +666,7 @@ def user_index_widgets_lazy(request):
|
||||
|
||||
def user_index(request):
|
||||
widgets = []
|
||||
for r, result in user_dashboard_widgets.send(request, user=request.user):
|
||||
for r, result in user_dashboard_widgets.send(request, skip_exceptions=True, user=request.user):
|
||||
widgets.extend(result)
|
||||
|
||||
ctx = {
|
||||
|
||||
@@ -65,7 +65,6 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
)
|
||||
maxfilesize = settings.FILE_UPLOAD_MAX_SIZE_IMAGE
|
||||
minfilesize = 10
|
||||
checkin_qr_warning = False
|
||||
title = None
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
@@ -293,7 +292,6 @@ class BaseEditorView(EventPermissionRequiredMixin, TemplateView):
|
||||
ctx['title'] = self.title
|
||||
ctx['locales'] = [p for p in settings.LANGUAGES if p[0] in self.request.event.settings.locales]
|
||||
ctx['maxfilesize'] = self.maxfilesize
|
||||
ctx['checkin_qr_warning'] = self.checkin_qr_warning
|
||||
return ctx
|
||||
|
||||
|
||||
|
||||
@@ -235,7 +235,6 @@ class LayoutGetDefault(EventPermissionRequiredMixin, View):
|
||||
|
||||
|
||||
class LayoutEditorView(BaseEditorView):
|
||||
checkin_qr_warning = True
|
||||
|
||||
@cached_property
|
||||
def layout(self):
|
||||
|
||||
+1
-35
@@ -19,18 +19,12 @@
|
||||
# You should have received a copy of the GNU Affero General Public License along with this program. If not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
import re
|
||||
import typing
|
||||
import weakref
|
||||
|
||||
from celery.exceptions import Retry
|
||||
from sentry_sdk import Hub
|
||||
from sentry_sdk.integrations import django as djangosentry
|
||||
from sentry_sdk.scrubber import EventScrubber
|
||||
from sentry_sdk.utils import AnnotatedValue, capture_internal_exceptions
|
||||
|
||||
if typing.TYPE_CHECKING:
|
||||
from sentry_sdk._types import Event
|
||||
from sentry_sdk.utils import capture_internal_exceptions
|
||||
|
||||
|
||||
def _make_event_processor(weak_request, integration):
|
||||
@@ -102,31 +96,3 @@ def setup_custom_filters():
|
||||
hub = Hub.current
|
||||
with hub.configure_scope() as scope:
|
||||
scope.add_event_processor(ignore_retry)
|
||||
|
||||
|
||||
class PretixEventScrubber(EventScrubber):
|
||||
secret_re = re.compile(
|
||||
'.*(pretixsecret|ramiiosecret|PRIVATE KEY|://[^/@:]*:[^/@:]+@).*',
|
||||
re.IGNORECASE
|
||||
)
|
||||
|
||||
def scrub_dict(self, d: object) -> None:
|
||||
if not isinstance(d, dict):
|
||||
return
|
||||
|
||||
super().scrub_dict(d)
|
||||
self._scrub_dict_for_known_secrets(d)
|
||||
|
||||
def _scrub_dict_for_known_secrets(self, d: dict):
|
||||
for k, v in d.items():
|
||||
if isinstance(v, str) and self.secret_re.match(v):
|
||||
d[k] = AnnotatedValue.substituted_because_contains_sensitive_data()
|
||||
|
||||
def scrub_exception(self, event: "Event") -> None:
|
||||
with capture_internal_exceptions():
|
||||
if "exception" in event:
|
||||
self.scrub_dict(event["exception"])
|
||||
|
||||
def scrub_event(self, event: "Event") -> None:
|
||||
super().scrub_event(event)
|
||||
self.scrub_exception(event)
|
||||
@@ -721,9 +721,9 @@ if config.has_option('sentry', 'dsn') and not any(c in sys.argv for c in ('shell
|
||||
from sentry_sdk.integrations.logging import (
|
||||
LoggingIntegration, ignore_logger,
|
||||
)
|
||||
from sentry_sdk.scrubber import DEFAULT_DENYLIST
|
||||
from sentry_sdk.scrubber import DEFAULT_DENYLIST, EventScrubber
|
||||
|
||||
from .sentry import PretixSentryIntegration, PretixEventScrubber, setup_custom_filters
|
||||
from .sentry import PretixSentryIntegration, setup_custom_filters
|
||||
|
||||
SENTRY_TOKEN = config.get('sentry', 'traces_sample_token', fallback='')
|
||||
SENTRY_ENABLE_LOGS = config.getboolean('sentry', 'enable_logs', fallback=False)
|
||||
@@ -759,7 +759,7 @@ if config.has_option('sentry', 'dsn') and not any(c in sys.argv for c in ('shell
|
||||
traces_sampler=traces_sampler,
|
||||
environment=urlparse(SITE_URL).netloc,
|
||||
release=__version__,
|
||||
event_scrubber=PretixEventScrubber(denylist=pretix_denylist, recursive=True),
|
||||
event_scrubber=EventScrubber(denylist=pretix_denylist, recursive=True),
|
||||
send_default_pii=False,
|
||||
propagate_traces=False, # see https://github.com/getsentry/sentry-python/issues/1717
|
||||
)
|
||||
|
||||
@@ -618,21 +618,18 @@ var editor = {
|
||||
$('#loading-initial').remove()
|
||||
})
|
||||
}
|
||||
editor._update_notices()
|
||||
editor._update_version_notice()
|
||||
},
|
||||
|
||||
_update_notices: function () {
|
||||
_update_version_notice: function () {
|
||||
$('#version-notice').toggle(
|
||||
editor._other_page_objects.some((o) => o.type === 'textcontainer')
|
||||
|| editor.fabric.getObjects().some((o) => o.type === 'textcontainer')
|
||||
)
|
||||
$('#nocheckin-notice').toggle(
|
||||
!editor.fabric.getObjects().some((o) => o.type === 'barcodearea' && (!o.content || o.content === 'secret'))
|
||||
)
|
||||
},
|
||||
|
||||
_update_toolbox_values: function () {
|
||||
editor._update_notices()
|
||||
editor._update_version_notice()
|
||||
let o = editor.fabric.getActiveObject()
|
||||
if (!o) {
|
||||
return
|
||||
@@ -901,7 +898,6 @@ var editor = {
|
||||
|
||||
o.setCoords()
|
||||
editor.fabric.renderAll()
|
||||
editor._update_notices()
|
||||
},
|
||||
|
||||
_update_toolbox: function () {
|
||||
|
||||
@@ -103,6 +103,3 @@ body {
|
||||
.panel-pdf-editor .panel-heading form > .btn-group {
|
||||
margin-right: 1em;
|
||||
}
|
||||
#nocheckin-notice {
|
||||
display: none;
|
||||
}
|
||||
@@ -26,7 +26,6 @@ from unittest import mock
|
||||
|
||||
import pytest
|
||||
from django.core import mail as djmail
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.core.files.base import ContentFile
|
||||
from django.utils.timezone import now
|
||||
from django_countries.fields import Country
|
||||
@@ -249,14 +248,7 @@ def test_order_update_state_validation(token_client, organizer, event, order):
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_order_update_transmission_validation(token_client, organizer, event, order, monkeypatch):
|
||||
def mock_validate(self, value):
|
||||
if value == "9930:DE111111111":
|
||||
return value
|
||||
raise ValidationError("Invalid")
|
||||
|
||||
monkeypatch.setattr("pretix.base.invoicing.peppol.PeppolIdValidator._validate_peppol_id_online", mock_validate)
|
||||
|
||||
def test_order_update_transmission_validation(token_client, organizer, event, order):
|
||||
resp = token_client.patch(
|
||||
'/api/v1/organizers/{}/events/{}/orders/{}/'.format(
|
||||
organizer.slug, event.slug, order.code
|
||||
@@ -382,7 +374,7 @@ def test_order_update_transmission_validation(token_client, organizer, event, or
|
||||
"vat_id": "",
|
||||
"transmission_type": "peppol",
|
||||
"transmission_info": {
|
||||
"transmission_peppol_participant_id": "9930:DE111111111",
|
||||
"transmission_peppol_participant_id": "9930:DE811569869",
|
||||
"ignored": "parameter",
|
||||
},
|
||||
}
|
||||
@@ -392,7 +384,7 @@ def test_order_update_transmission_validation(token_client, organizer, event, or
|
||||
order.invoice_address.refresh_from_db()
|
||||
assert order.invoice_address.transmission_type == "peppol"
|
||||
assert order.invoice_address.transmission_info == {
|
||||
"transmission_peppol_participant_id": "9930:DE111111111",
|
||||
"transmission_peppol_participant_id": "9930:DE811569869",
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in new issue
Block a user