Add pluggable ticket secret generators (#1809)

This commit is contained in:
Raphael Michel
2020-10-19 15:00:55 +02:00
committed by GitHub
parent 6e20f33ef5
commit 22bba28bea
43 changed files with 890 additions and 69 deletions
+3
View File
@@ -37,6 +37,7 @@ class PretixScanSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:checkinlist-status'),
('GET', 'api-v1:checkinlistpos-list'),
('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:order-list'),
('GET', 'api-v1:event.settings'),
)
@@ -61,6 +62,7 @@ class PretixScanNoSyncSecurityProfile(AllowListSecurityProfile):
('GET', 'api-v1:checkinlist-list'),
('GET', 'api-v1:checkinlist-status'),
('POST', 'api-v1:checkinlistpos-redeem'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:event.settings'),
)
@@ -98,6 +100,7 @@ class PretixPosSecurityProfile(AllowListSecurityProfile):
('POST', 'plugins:pretix_posbackend:posclosing-list'),
('POST', 'plugins:pretix_posbackend:posdebugdump-list'),
('POST', 'plugins:pretix_posbackend:stripeterminal.token'),
('GET', 'api-v1:revokedsecrets-list'),
('GET', 'api-v1:event.settings'),
)
+23 -1
View File
@@ -95,19 +95,41 @@ class TimeZoneField(ChoiceField):
)
class ValidKeysField(Field):
def to_representation(self, value):
return value.cache.get_or_set(
'ticket_secret_valid_keys',
lambda: self._get(value),
120
)
def _get(self, value):
return {
'pretix_sig1': [
value.settings.ticket_secrets_pretix_sig1_pubkey
] if value.settings.ticket_secrets_pretix_sig1_pubkey else []
}
class EventSerializer(I18nAwareModelSerializer):
meta_data = MetaDataField(required=False, source='*')
item_meta_properties = MetaPropertyField(required=False, source='*')
plugins = PluginsField(required=False, source='*')
seat_category_mapping = SeatCategoryMappingField(source='*', required=False)
timezone = TimeZoneField(required=False, choices=[(a, a) for a in common_timezones])
valid_keys = ValidKeysField(source='*', read_only=True)
class Meta:
model = Event
fields = ('name', 'slug', 'live', 'testmode', 'currency', 'date_from',
'date_to', 'date_admission', 'is_public', 'presale_start',
'presale_end', 'location', 'geo_lat', 'geo_lon', 'has_subevents', 'meta_data', 'seating_plan',
'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties')
'plugins', 'seat_category_mapping', 'timezone', 'item_meta_properties', 'valid_keys')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if not hasattr(self.context['request'], 'event'):
self.fields.pop('valid_keys')
def validate(self, data):
data = super().validate(data)
+8 -1
View File
@@ -21,7 +21,7 @@ from pretix.base.models import (
OrderPosition, Question, QuestionAnswer, Seat, SubEvent, TaxRule, Voucher,
)
from pretix.base.models.orders import (
CartPosition, OrderFee, OrderPayment, OrderRefund,
CartPosition, OrderFee, OrderPayment, OrderRefund, RevokedTicketSecret,
)
from pretix.base.pdf import get_variables
from pretix.base.services.cart import error_messages
@@ -1209,3 +1209,10 @@ class OrderRefundCreateSerializer(I18nAwareModelSerializer):
order = OrderRefund(order=self.context['order'], payment=p, **validated_data)
order.save()
return order
class RevokedTicketSecretSerializer(I18nAwareModelSerializer):
class Meta:
model = RevokedTicketSecret
fields = ('id', 'secret', 'created')
+1
View File
@@ -39,6 +39,7 @@ event_router.register(r'vouchers', voucher.VoucherViewSet)
event_router.register(r'orders', order.OrderViewSet)
event_router.register(r'orderpositions', order.OrderPositionViewSet)
event_router.register(r'invoices', order.InvoiceViewSet)
event_router.register(r'revokedsecrets', order.RevokedSecretViewSet, basename='revokedsecrets')
event_router.register(r'taxrules', event.TaxRuleViewSet)
event_router.register(r'waitinglistentries', waitinglist.WaitingListViewSet)
event_router.register(r'checkinlists', checkin.CheckinListViewSet)
+13 -2
View File
@@ -278,13 +278,23 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
else:
op = queryset.get(secret=self.kwargs['pk'])
except OrderPosition.DoesNotExist:
self.request.event.log_action('pretix.event.checkin.unknown', data={
revoked_matches = list(self.request.event.revoked_secrets.filter(secret=self.kwargs['pk']))
if len(revoked_matches) == 0 or not force:
self.request.event.log_action('pretix.event.checkin.unknown', data={
'datetime': dt,
'type': type,
'list': self.checkinlist.pk,
'barcode': self.kwargs['pk']
}, user=self.request.user, auth=self.request.auth)
raise Http404()
op = revoked_matches[0].position
op.order.log_action('pretix.event.checkin.revoked', data={
'datetime': dt,
'type': type,
'list': self.checkinlist.pk,
'barcode': self.kwargs['pk']
}, user=self.request.user, auth=self.request.auth)
raise Http404()
given_answers = {}
if 'answers' in self.request.data:
@@ -325,6 +335,7 @@ class CheckinListPositionViewSet(viewsets.ReadOnlyModelViewSet):
'position': op.id,
'positionid': op.positionid,
'errorcode': e.code,
'force': force,
'datetime': dt,
'type': type,
'list': self.checkinlist.pk
-1
View File
@@ -89,7 +89,6 @@ class EventViewSet(viewsets.ModelViewSet):
)
qs = filter_qs_by_attr(qs, self.request)
return qs.prefetch_related(
'meta_values', 'meta_values__property', 'seat_category_mappings'
)
+31 -4
View File
@@ -26,15 +26,18 @@ from pretix.api.serializers.order import (
InvoiceSerializer, OrderCreateSerializer, OrderPaymentCreateSerializer,
OrderPaymentSerializer, OrderPositionSerializer,
OrderRefundCreateSerializer, OrderRefundSerializer, OrderSerializer,
PriceCalcSerializer, SimulatedOrderSerializer,
PriceCalcSerializer, RevokedTicketSecretSerializer,
SimulatedOrderSerializer,
)
from pretix.base.i18n import language
from pretix.base.models import (
CachedCombinedTicket, CachedTicket, Device, Event, Invoice, InvoiceAddress,
Order, OrderFee, OrderPayment, OrderPosition, OrderRefund, Quota, SubEvent,
TeamAPIToken, generate_position_secret, generate_secret,
TeamAPIToken, generate_secret,
)
from pretix.base.models.orders import RevokedTicketSecret
from pretix.base.payment import PaymentException
from pretix.base.secrets import assign_ticket_secret
from pretix.base.services import tickets
from pretix.base.services.invoices import (
generate_cancellation, generate_invoice, invoice_pdf, invoice_qualified,
@@ -483,8 +486,9 @@ class OrderViewSet(viewsets.ModelViewSet):
order = self.get_object()
order.secret = generate_secret()
for op in order.all_positions.all():
op.secret = generate_position_secret()
op.save()
assign_ticket_secret(
request.event, op, force_invalidate=True, save=True
)
order.save(update_fields=['secret'])
CachedTicket.objects.filter(order_position__order=order).delete()
CachedCombinedTicket.objects.filter(order=order).delete()
@@ -1298,3 +1302,26 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
auth=self.request.auth,
)
return Response(status=204)
with scopes_disabled():
class RevokedSecretFilter(FilterSet):
created_since = django_filters.IsoDateTimeFilter(field_name='created', lookup_expr='gte')
class Meta:
model = RevokedTicketSecret
fields = []
class RevokedSecretViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = RevokedTicketSecretSerializer
queryset = RevokedTicketSecret.objects.none()
filter_backends = (DjangoFilterBackend, OrderingFilter)
ordering = ('-created',)
ordering_fields = ('created', 'secret')
filterset_class = RevokedSecretFilter
permission = 'can_view_orders'
write_permission = 'can_change_orders'
def get_queryset(self):
return RevokedTicketSecret.objects.filter(event=self.request.event)