Skip to content

Commit 33fb0ac

Browse files
committed
Fix: prevent unauthorized manual check-ins in dashboard
1 parent 7b5cc99 commit 33fb0ac

File tree

1 file changed

+125
-1
lines changed

1 file changed

+125
-1
lines changed

src/tests/control/test_checkins.py

Lines changed: 125 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,11 +313,26 @@ def test_checkins_list_mixed(client, checkin_list_env, query, expected):
313313
assert item_keys == expected
314314

315315

316+
def _validate_position_for_checkin_list(position, checkin_list):
317+
"""
318+
Validate that an OrderPosition belongs to the event and is allowed in the check-in list.
319+
"""
320+
if position.order.event != checkin_list.event:
321+
return False
322+
323+
if checkin_list.all_products:
324+
return True
325+
326+
return position.item in checkin_list.limit_products.all()
327+
328+
316329
@pytest.mark.django_db
317330
def test_manual_checkins(client, checkin_list_env):
318331
client.login(email='[email protected]', password='dummy')
319332
with scopes_disabled():
320333
assert not checkin_list_env[5][3].checkins.exists()
334+
335+
# Test with valid position
321336
client.post(
322337
'/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_env[6].pk),
323338
{'checkin': [checkin_list_env[5][3].pk]},
@@ -329,6 +344,60 @@ def test_manual_checkins(client, checkin_list_env):
329344
).exists()
330345

331346

347+
@pytest.mark.django_db
348+
def test_manual_checkins_unauthorized_position(client, checkin_list_env):
349+
client.login(email='[email protected]', password='dummy')
350+
351+
# Create a position from a different event that shouldn't be checkable
352+
other_orga = Organizer.objects.create(name='Other', slug='other')
353+
other_event = Event.objects.create(
354+
organizer=other_orga,
355+
name='Other Event',
356+
slug='other',
357+
date_from=now(),
358+
)
359+
other_item = Item.objects.create(event=other_event, name='Other Ticket', default_price=23, admission=True)
360+
other_order = Order.objects.create(
361+
code='OTHER',
362+
event=other_event,
363+
364+
status=Order.STATUS_PAID,
365+
datetime=now(),
366+
expires=now() + timedelta(days=10),
367+
total=23,
368+
locale='en',
369+
)
370+
other_position = OrderPosition.objects.create(
371+
order=other_order,
372+
item=other_item,
373+
variation=None,
374+
price=Decimal('23'),
375+
attendee_name_parts={'full_name': 'Other'},
376+
)
377+
378+
# Try to check in the unauthorized position
379+
with scopes_disabled():
380+
assert not _validate_position_for_checkin_list(other_position, checkin_list_env[6])
381+
initial_checkin_count = Checkin.objects.count()
382+
initial_log_count = LogEntry.objects.count()
383+
384+
client.post(
385+
'/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_env[6].pk),
386+
{'checkin': [other_position.pk]},
387+
)
388+
389+
# Verify the unauthorized position was not checked in
390+
with scopes_disabled():
391+
assert Checkin.objects.count() == initial_checkin_count
392+
assert not other_position.checkins.exists()
393+
# Verify no log entries were created for the unauthorized operation
394+
assert LogEntry.objects.count() == initial_log_count
395+
assert not LogEntry.objects.filter(
396+
action_type='pretix.event.checkin',
397+
object_id=other_position.order.pk
398+
).exists()
399+
400+
332401
@pytest.mark.django_db
333402
def test_manual_checkins_revert(client, checkin_list_env):
334403
client.login(email='[email protected]', password='dummy')
@@ -353,6 +422,61 @@ def test_manual_checkins_revert(client, checkin_list_env):
353422
).exists()
354423

355424

425+
@pytest.mark.django_db
426+
def test_manual_checkins_revert_unauthorized_position(client, checkin_list_env):
427+
client.login(email='[email protected]', password='dummy')
428+
429+
# Create a position from a different event
430+
other_orga = Organizer.objects.create(name='Other', slug='other')
431+
other_event = Event.objects.create(
432+
organizer=other_orga,
433+
name='Other Event',
434+
slug='other',
435+
date_from=now(),
436+
)
437+
other_item = Item.objects.create(event=other_event, name='Other Ticket', default_price=23, admission=True)
438+
other_order = Order.objects.create(
439+
code='OTHER',
440+
event=other_event,
441+
442+
status=Order.STATUS_PAID,
443+
datetime=now(),
444+
expires=now() + timedelta(days=10),
445+
total=23,
446+
locale='en',
447+
)
448+
other_position = OrderPosition.objects.create(
449+
order=other_order,
450+
item=other_item,
451+
variation=None,
452+
price=Decimal('23'),
453+
attendee_name_parts={'full_name': 'Other'},
454+
)
455+
456+
# Create a checkin for the unauthorized position (simulating it was somehow created)
457+
with scopes_disabled():
458+
Checkin.objects.create(position=other_position, list=checkin_list_env[6])
459+
initial_checkin_count = Checkin.objects.count()
460+
initial_log_count = LogEntry.objects.count()
461+
462+
# Try to revert the unauthorized position
463+
client.post(
464+
'/control/event/dummy/dummy/checkinlists/{}/'.format(checkin_list_env[6].pk),
465+
{'checkin': [other_position.pk], 'revert': 'true'},
466+
)
467+
468+
# Verify the unauthorized position checkin was not reverted
469+
with scopes_disabled():
470+
assert Checkin.objects.count() == initial_checkin_count
471+
assert other_position.checkins.exists()
472+
# Verify no log entries were created for the unauthorized operation
473+
assert LogEntry.objects.count() == initial_log_count
474+
assert not LogEntry.objects.filter(
475+
action_type='pretix.event.checkin.reverted',
476+
object_id=other_position.order.pk
477+
).exists()
478+
479+
356480
@pytest.fixture
357481
def checkin_list_with_addon_env():
358482
# permission
@@ -574,4 +698,4 @@ def test_delete(self):
574698
assert doc.select('.alert-success')
575699
self.assertNotIn('VAT', doc.select('#page-wrapper')[0].text)
576700
with scopes_disabled():
577-
assert not self.event1.checkin_lists.exists()
701+
assert not self.event1.checkin_lists.exists()

0 commit comments

Comments
 (0)