Pluggable permissions (#5728)

* Data model draft

* Refactor query and assignment usages of old permissions

* Backend UI

* API serializer

* Big string replace

* Docs, tests and fixes for teams api

* Update docs for device auth

* Eliminate old names

* Make tests pass

* Use new permissions, remove inconsistencies

* Add test for translations

* Show plugin permissions

* Add permission for seating plans

* Fix plugin activation

* Fix failing test

* Refactor to permission groups

* Update doc/api/resources/devices.rst

Co-authored-by: luelista <weller@rami.io>

* Update doc/api/resources/events.rst

Co-authored-by: luelista <weller@rami.io>

* Update src/pretix/api/serializers/organizer.py

Co-authored-by: luelista <weller@rami.io>

* Fix typo

* Fix python version compat

* Replacement after rebase

* Add proper permission handling for exports

* Docs for exporters

* Runtime linting of permission names

* Fix typos

* Show export page even without orders permission

* More legacy compat

* Do not strongly validate before plugins are loaded

* Rebase migration

* Add permission for outgoing mails

* Review notes

* Update doc/api/resources/teams.rst

Co-authored-by: Richard Schreiber <schreiber@pretix.eu>

* Clean up logic around exporters

* Review and failures

* Fix migration leading to forbidden combination

* Handle permissions on event copying

* Remove print-statements

* Make test clearer

* Review feedback

* Add AnyPermissionOf

* migration safety

---------

Co-authored-by: luelista <weller@rami.io>
Co-authored-by: Richard Schreiber <schreiber@pretix.eu>
This commit is contained in:
Raphael Michel
2026-03-17 14:43:56 +01:00
committed by GitHub
parent eddde2b6c0
commit df0b580dd6
203 changed files with 5374 additions and 2331 deletions

View File

@@ -1123,7 +1123,7 @@ class Obligatory2FATest(TestCase):
session.save()
organizer = Organizer.objects.create(name='Dummy', slug='dummy')
team = Team.objects.create(organizer=organizer, can_change_teams=True, name='Admin team')
team = Team.objects.create(organizer=organizer, all_event_permissions=True, name='Admin team')
team.members.add(self.user)
self.user.require_2fa = False
self.user.save()

View File

@@ -61,7 +61,7 @@ def dashboard_env():
item_ticket = Item.objects.create(event=event, name="Ticket", default_price=23, admission=True)
item_mascot = Item.objects.create(event=event, name="Mascot", default_price=10, admission=False)
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True)
t = Team.objects.create(organizer=o, all_event_permissions=True)
t.members.add(user)
t.limit_events.add(event)
@@ -139,7 +139,7 @@ def checkin_list_env():
# permission
orga = Organizer.objects.create(name='Dummy', slug='dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
team = Team.objects.create(organizer=orga, can_view_orders=True, can_change_orders=True)
team = Team.objects.create(organizer=orga, all_event_permissions=True)
team.members.add(user)
# event
@@ -321,7 +321,7 @@ def test_manual_checkins_revert_requires_order_change_permission(client, checkin
client.login(email='dummy@dummy.dummy', password='dummy')
with scopes_disabled():
assert not checkin_list_env[5][3].checkins.exists()
Team.objects.update(can_change_orders=False, can_checkin_orders=True)
Team.objects.update(all_event_permissions=False, limit_event_permissions={"event.orders:checkin": True})
client.post('/control/event/dummy/dummy/checkinlists/{}/bulk_action'.format(checkin_list_env[6].pk), {
'checkin': [checkin_list_env[5][3].pk]
})
@@ -363,7 +363,7 @@ def checkin_list_with_addon_env():
# permission
orga = Organizer.objects.create(name='Dummy', slug='dummy')
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
team = Team.objects.create(organizer=orga, can_view_orders=True, can_change_orders=True)
team = Team.objects.create(organizer=orga, all_event_permissions=True)
team.members.add(user)
# event
@@ -466,7 +466,7 @@ class CheckinListFormTest(SoupTest):
date_from=datetime(2013, 12, 26, tzinfo=timezone.utc),
)
self.event1.settings.timezone = 'Europe/Berlin'
t = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_view_orders=True)
t = Team.objects.create(organizer=self.orga1, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.client.login(email='dummy@dummy.dummy', password='dummy')

View File

@@ -85,7 +85,7 @@ def order(event, customer):
def admin_user(organizer):
u = User.objects.create_user('dummy@dummy.dummy', 'dummy')
admin_team = Team.objects.create(
organizer=organizer, can_manage_customers=True, can_change_organizer_settings=True,
organizer=organizer, all_organizer_permissions=True,
name='Admin team'
)
admin_team.members.add(u)

View File

@@ -55,7 +55,7 @@ def admin_user(admin_team):
@pytest.fixture
def admin_team(organizer):
return Team.objects.create(organizer=organizer, can_change_organizer_settings=True, name='Admin team')
return Team.objects.create(organizer=organizer, all_organizer_permissions=True, name='Admin team')
@pytest.mark.django_db

View File

@@ -76,13 +76,16 @@ class EventsTest(SoupTest):
date_from=datetime.datetime(2014, 9, 5, tzinfo=datetime.timezone.utc),
)
self.team1 = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
can_change_items=True)
self.team1 = Team.objects.create(
organizer=self.orga1,
name="T1",
all_event_permissions=True,
limit_organizer_permissions={"organizer.events:create": True}
)
self.team1.members.add(self.user)
self.team1.limit_events.add(self.event1)
self.team2 = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_change_items=True,
can_change_orders=True, can_change_vouchers=True)
self.team2 = Team.objects.create(organizer=self.orga1, name="T2", all_event_permissions=True)
self.team2.members.add(self.user)
self.client.login(email='dummy@dummy.dummy', password='dummy')
@@ -339,7 +342,7 @@ class EventsTest(SoupTest):
self.orga1.refresh_from_db()
assert "tests.testdummyhybrid" not in self.orga1.plugins
t2 = Team.objects.create(organizer=self.orga1, can_change_organizer_settings=True)
t2 = Team.objects.create(organizer=self.orga1, all_organizer_permissions=True, all_event_permissions=True)
t2.members.add(self.user)
self.post_doc('/control/event/%s/%s/settings/plugins' % (self.orga1.slug, self.event1.slug),
@@ -1262,6 +1265,265 @@ class EventsTest(SoupTest):
})
assert doc.select(".has-error")
def test_create_event_copy_from_other_org_validates_source_permissions(self):
# To prevent leaks of e.g. settings contents, a user may only copy from one organizer to the other
# if they have basically all permissions on the old event for all data that may be copied.
self.team1.all_event_permissions = False
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
self.team1.save()
team3 = Team.objects.create(organizer=self.orga2, all_event_permissions=True, all_organizer_permissions=True)
team3.members.add(self.user)
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'event_wizard-prefix': 'event_wizard',
'foundation-organizer': self.orga2.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select("#id_basics-name_0")
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'event_wizard-prefix': 'event_wizard',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from_0': '2016-12-27',
'basics-date_from_1': '10:00:00',
'basics-date_to_0': '2016-12-30',
'basics-date_to_1': '19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-tax_rate': '19.00',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start_0': '2016-11-01',
'basics-presale_start_1': '10:00:00',
'basics-presale_end_0': '2016-11-30',
'basics-presale_end_1': '18:00:00',
})
assert doc.select("#id_copy-copy_from_event")
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'event_wizard-prefix': 'event_wizard',
'copy-copy_from_event': self.event1.pk
})
assert doc.select(".alert-danger")
assert "sufficient level of access" in doc.select(".has-error")[0].text
def test_create_event_clone_from_other_org_validates_source_permissions(self):
# To prevent leaks of e.g. settings contents, a user may only copy from one organizer to the other
# if they have basically all permissions on the old event for all data that may be copied.
self.team1.all_event_permissions = False
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
self.team1.save()
team3 = Team.objects.create(organizer=self.orga2, all_event_permissions=True, all_organizer_permissions=True)
team3.members.add(self.user)
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
'event_wizard-current_step': 'foundation',
'event_wizard-prefix': 'event_wizard',
'foundation-organizer': self.orga2.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select(".alert-danger")
assert "sufficient level of access" in doc.select(".has-error")[0].text
def test_create_event_copy_from_same_org_creates_new_team_with_same_permissions(self):
# To prevent unwanted permission escalations, when a user copies an event and a new team is created to make
# sure they can access the new event, the new event must be created with the same level of access they have
# on the old event.
self.team1.all_event_permissions = False
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
self.team1.save()
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'event_wizard-prefix': 'event_wizard',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select("#id_basics-name_0")
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'event_wizard-prefix': 'event_wizard',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from_0': '2016-12-27',
'basics-date_from_1': '10:00:00',
'basics-date_to_0': '2016-12-30',
'basics-date_to_1': '19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-tax_rate': '19.00',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start_0': '2016-11-01',
'basics-presale_start_1': '10:00:00',
'basics-presale_end_0': '2016-11-30',
'basics-presale_end_1': '18:00:00',
})
assert doc.select("#id_copy-copy_from_event")
self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'event_wizard-prefix': 'event_wizard',
'copy-copy_from_event': self.event1.pk
})
with scopes_disabled():
ev = Event.objects.get(slug='33c3')
new_team = Team.objects.get(limit_events=ev, members=self.user)
assert new_team.pk > self.team2.pk
assert new_team.all_event_permissions is False
assert new_team.all_organizer_permissions is False
assert new_team.limit_event_permissions == {"event.settings.general:write": True, "event.orders:read": True}
assert new_team.limit_organizer_permissions == {}
assert new_team.all_events is False
assert new_team.limit_events.get() == ev
def test_create_event_clone_from_same_org_creates_new_team_with_same_permissions(self):
# To prevent unwanted permission escalations, when a user copies an event and a new team is created to make
# sure they can access the new event, the new event must be created with the same level of access they have
# on the old event.
self.team1.all_event_permissions = False
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
self.team1.save()
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
'event_wizard-current_step': 'foundation',
'event_wizard-prefix': 'event_wizard',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select("#id_basics-name_0")
self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
'event_wizard-current_step': 'basics',
'event_wizard-prefix': 'event_wizard',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from_0': '2016-12-27',
'basics-date_from_1': '10:00:00',
'basics-date_to_0': '2016-12-30',
'basics-date_to_1': '19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-tax_rate': '19.00',
'basics-locale': 'en',
'basics-timezone': 'Europe/Berlin',
'basics-presale_start_0': '2016-11-01',
'basics-presale_start_1': '10:00:00',
'basics-presale_end_0': '2016-11-30',
'basics-presale_end_1': '18:00:00',
})
with scopes_disabled():
ev = Event.objects.get(slug='33c3')
new_team = Team.objects.get(limit_events=ev, members=self.user)
assert new_team.pk > self.team2.pk
assert new_team.all_event_permissions is False
assert new_team.all_organizer_permissions is False
assert new_team.limit_event_permissions == {"event.settings.general:write": True, "event.orders:read": True}
assert new_team.limit_organizer_permissions == {}
assert new_team.all_events is False
assert new_team.limit_events.get() == ev
def test_create_event_copy_from_same_org_validates_selected_team_permissions(self):
# To prevent unwanted permission escalations, when a user copies an event and selects the team the new event
# should be attached to, this new team may not have higher permissions than the permissions the user holds for
# the event that is copied from.
self.team1.all_event_permissions = False
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
self.team1.save()
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'foundation',
'event_wizard-prefix': 'event_wizard',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select("#id_basics-name_0")
assert doc.select("#id_basics-team")
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'basics',
'event_wizard-prefix': 'event_wizard',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from_0': '2016-12-27',
'basics-date_from_1': '10:00:00',
'basics-date_to_0': '2016-12-30',
'basics-date_to_1': '19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-tax_rate': '19.00',
'basics-locale': 'en',
'basics-team': self.team2.pk,
'basics-timezone': 'Europe/Berlin',
'basics-presale_start_0': '2016-11-01',
'basics-presale_start_1': '10:00:00',
'basics-presale_end_0': '2016-11-30',
'basics-presale_end_1': '18:00:00',
})
assert doc.select("#id_copy-copy_from_event")
doc = self.post_doc('/control/events/add', {
'event_wizard-current_step': 'copy',
'event_wizard-prefix': 'event_wizard',
'copy-copy_from_event': self.event1.pk
})
assert doc.select(".alert-danger")
assert "less access than" in doc.select(".has-error")[0].text
def test_create_event_clone_from_same_org_validates_selected_team_permissions(self):
# To prevent unwanted permission escalations, when a user copies an event and selects the team the new event
# should be attached to, this new team may not have higher permissions than the permissions the user holds for
# the event that is copied from.
self.team1.all_event_permissions = False
self.team1.limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
self.team1.save()
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
'event_wizard-current_step': 'foundation',
'event_wizard-prefix': 'event_wizard',
'foundation-organizer': self.orga1.pk,
'foundation-locales': ('en', 'de')
})
assert doc.select("#id_basics-name_0")
assert doc.select("#id_basics-team")
doc = self.post_doc(f'/control/events/add?clone={self.event1.pk}', {
'event_wizard-current_step': 'basics',
'event_wizard-prefix': 'event_wizard',
'basics-name_0': '33C3',
'basics-name_1': '33C3',
'basics-slug': '33c3',
'basics-date_from_0': '2016-12-27',
'basics-date_from_1': '10:00:00',
'basics-date_to_0': '2016-12-30',
'basics-date_to_1': '19:00:00',
'basics-location_0': 'Hamburg',
'basics-location_1': 'Hamburg',
'basics-currency': 'EUR',
'basics-tax_rate': '19.00',
'basics-locale': 'en',
'basics-team': self.team2.pk,
'basics-timezone': 'Europe/Berlin',
'basics-presale_start_0': '2016-11-01',
'basics-presale_start_1': '10:00:00',
'basics-presale_end_0': '2016-11-30',
'basics-presale_end_1': '18:00:00',
})
assert "would give you more access than" in doc.select(".has-error")[0].text
class EventDeletionTest(SoupTest):
@scopes_disabled()
@@ -1276,8 +1538,7 @@ class EventDeletionTest(SoupTest):
has_subevents=False
)
t = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
can_change_items=True)
t = Team.objects.create(organizer=self.orga1, all_organizer_permissions=True, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.ticket = self.event1.items.create(name='Early-bird ticket',

View File

@@ -23,7 +23,9 @@ import datetime
import json
import pytest
from bs4 import BeautifulSoup
from django.utils.timezone import now
from tests.base import extract_form_fields
from pretix.base.models import (
Event, Item, Organizer, ScheduledEventExport, ScheduledOrganizerExport,
@@ -40,8 +42,7 @@ def env():
)
event.settings.set("ticketoutput_testdummy__enabled", True)
user = User.objects.create_user("dummy@dummy.dummy", "dummy")
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True, can_manage_customers=True,
can_change_event_settings=True)
t = Team.objects.create(organizer=o, all_event_permissions=True)
t.members.add(user)
t.limit_events.add(event)
@@ -163,7 +164,8 @@ def test_event_export_schedule(client, env):
@pytest.mark.django_db(transaction=True)
def test_event_limited_permission(client, env):
env[2].can_change_event_settings = False
env[2].all_event_permissions = False
env[2].limit_event_permissions = {"event.orders:read": True}
env[2].save()
user2 = User.objects.create_user("dummy2@dummy.dummy", "dummy")
@@ -199,7 +201,7 @@ def test_event_limited_permission(client, env):
response = client.get(f"/control/event/dummy/dummy/orders/export/{s2.pk}/delete")
assert response.status_code == 404
env[2].can_change_event_settings = True
env[2].limit_event_permissions = {"event.settings.general:write": True, "event.orders:read": True}
env[2].save()
response = client.get("/control/event/dummy/dummy/orders/export/")
assert b"RULE1" in response.content
@@ -330,7 +332,8 @@ def test_organizer_export_schedule(client, env):
@pytest.mark.django_db(transaction=True)
def test_organizer_limited_permission(client, env):
env[2].can_change_organizer_settings = False
env[2].all_organizer_permissions = False
env[2].all_event_permissions = True
env[2].save()
user2 = User.objects.create_user("dummy2@dummy.dummy", "dummy")
@@ -366,7 +369,7 @@ def test_organizer_limited_permission(client, env):
response = client.post(f"/control/organizer/dummy/export/{s2.pk}/run")
assert response.status_code == 404
env[2].can_change_organizer_settings = True
env[2].limit_organizer_permissions = {"organizer.settings.general:write": True}
env[2].save()
response = client.get("/control/organizer/dummy/export/")
assert b"RULE1" in response.content
@@ -377,3 +380,197 @@ def test_organizer_limited_permission(client, env):
assert response.status_code == 200
response = client.post(f"/control/organizer/dummy/export/{s2.pk}/run")
assert response.status_code == 302
def _can_see_but_not_edit_org_export(client, user, scheduled):
client.login(email=user.email, password="dummy")
response = client.get("/control/organizer/dummy/export/")
assert f"export/{scheduled.pk}/delete".encode() in response.content
response = client.get(f"/control/organizer/dummy/export/?identifier={scheduled.export_identifier}&scheduled={scheduled.pk}")
if response.status_code == 404:
return False
assert response.status_code == 200
doc = BeautifulSoup(response.content, "lxml")
form_data = extract_form_fields(doc.select("form[data-asynctask]")[0])
form_data["schedule"] = "save"
response = client.post(f"/control/organizer/dummy/export/?identifier={scheduled.export_identifier}&scheduled={scheduled.pk}",
data=form_data, follow=True)
assert response.status_code == 200
return b"alert-success" in response.content and b"does not have sufficient permission" not in response.content
@pytest.mark.django_db(transaction=True)
def test_organizer_edit_restrictions(client, env):
# This tests the prevention of a possible privilege escalation where user A creates a scheduled export and
# user B has settings permission (= they can see the export configuration), but not enough permission
# to run the export themselves. Without this check, user B could modify the export and add themselves
# as a recipient. Thereby, user B would gain access to data they can't have.
user1 = env[1]
user2 = User.objects.create_user("dummy2@dummy.dummy", "dummy")
event1 = env[0]
event2 = Event.objects.create(
organizer=env[0].organizer, name="Dummy", slug="dummy2",
date_from=now(), plugins="pretix.plugins.banktransfer,pretix.plugins.stripe,tests.testdummy"
)
team1 = env[2]
team1.all_organizer_permissions = False
team1.all_event_permissions = False
team1.all_events = False
team1.limit_organizer_permissions = {"organizer.settings.general:write": True}
team1.limit_event_permissions = {"event.orders:read": True, "event.settings.general:write": True}
team1.save()
team1.limit_events.add(event1)
team2 = env[0].organizer.teams.create(
all_organizer_permissions=False, all_event_permissions=False, all_events=False,
limit_event_permissions={"event.orders:read": True},
limit_organizer_permissions={"organizer.giftcards:read": True}
)
team2.limit_events.add(event2)
team2.members.add(user2)
# Scenario 1
# User 2 created an export for all events. User 2 can edit it, because they own it.
# User 1 can see it, because they have permission to see scheduled exports, but can't change it, because they
# don't have access to all events.
s1 = ScheduledOrganizerExport.objects.create(
organizer=env[0].organizer,
owner=user2,
export_identifier="dummy_orders",
export_form_data={"all_events": True, "events": []},
mail_subject="Test",
mail_template="Test",
schedule_rrule="DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO",
schedule_rrule_time=datetime.time(2, 30, 0)
)
assert _can_see_but_not_edit_org_export(client, user2, s1)
assert not _can_see_but_not_edit_org_export(client, user1, s1)
# Scenario 2
# User 2 created an export for all events. User 2 can edit it, because they own it.
# User 1 can see it, because they have permission to see scheduled exports, and change it, because they
# have access to all events.
team1.all_events = True
team1.save()
assert _can_see_but_not_edit_org_export(client, user2, s1)
assert _can_see_but_not_edit_org_export(client, user1, s1)
# Scenario 3
# User 2 created an export for a specific event. User 2 can edit it, because they own it.
# User 1 can see it, because they have permission to see scheduled exports, but can't change it, because they
# don't have access to that event.
team1.all_events = False
team1.save()
s1.export_form_data = {"all_events": False, "events": [event2.pk]}
s1.save()
assert _can_see_but_not_edit_org_export(client, user2, s1)
assert not _can_see_but_not_edit_org_export(client, user1, s1)
# Scenario 4
# User 2 created an export for a specific event. User 2 can edit it, because they own it.
# User 1 can see it, because they have permission to see scheduled exports, and change it, because they
# have access to that event.
team1.limit_events.add(event2)
assert _can_see_but_not_edit_org_export(client, user2, s1)
assert _can_see_but_not_edit_org_export(client, user1, s1)
# Scenario 5
# User 2 created an export that requires a special permission on organizer level
# user 1 can see it, because they have permission to see scheduled exports, but can't change it, because they lack
# that special permission
s2 = ScheduledOrganizerExport.objects.create(
organizer=env[0].organizer,
owner=user2,
export_identifier="giftcardlist",
mail_subject="Test",
mail_template="Test",
schedule_rrule="DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO",
schedule_rrule_time=datetime.time(2, 30, 0)
)
assert _can_see_but_not_edit_org_export(client, user2, s2)
assert not _can_see_but_not_edit_org_export(client, user1, s2)
# Scenario 6
# User 2 created an export that requires a special permission on organizer level
# user 1 can see it, because they have permission to see scheduled exports, and change it, because they have
# that special permission
team1.limit_organizer_permissions["organizer.giftcards:read"] = True
team1.save()
assert _can_see_but_not_edit_org_export(client, user2, s2)
assert _can_see_but_not_edit_org_export(client, user1, s2)
def _can_see_but_not_edit_event_export(client, user, scheduled):
client.login(email=user.email, password="dummy")
response = client.get("/control/event/dummy/dummy/orders/export/")
assert f"export/{scheduled.pk}/delete".encode() in response.content
response = client.get(f"/control/event/dummy/dummy/orders/export/?identifier={scheduled.export_identifier}&scheduled={scheduled.pk}")
if response.status_code == 404:
return False
assert response.status_code == 200
doc = BeautifulSoup(response.content, "lxml")
form_data = extract_form_fields(doc.select("form[data-asynctask]")[0])
form_data["schedule"] = "save"
response = client.post(f"/control/event/dummy/dummy/orders/export/?identifier={scheduled.export_identifier}&scheduled={scheduled.pk}",
data=form_data, follow=True)
assert response.status_code == 200
return b"alert-success" in response.content and b"does not have sufficient permission" not in response.content
@pytest.mark.django_db(transaction=True)
def test_event_edit_restrictions(client, env):
# This tests the prevention of a possible privilege escalation where user A creates a scheduled export and
# user B has settings permission (= they can see the export configuration), but not enough permission
# to run the export themselves. Without this check, user B could modify the export and add themselves
# as a recipient. Thereby, user B would gain access to data they can't have.
user1 = env[1]
user2 = User.objects.create_user("dummy2@dummy.dummy", "dummy")
event1 = env[0]
team1 = env[2]
team1.all_organizer_permissions = False
team1.all_event_permissions = False
team1.all_events = False
team1.limit_organizer_permissions = {"organizer.settings.general:write": True}
team1.limit_event_permissions = {"event.orders:read": True, "event.settings.general:write": True}
team1.save()
team1.limit_events.add(event1)
team2 = env[0].organizer.teams.create(
all_organizer_permissions=False, all_event_permissions=False, all_events=False,
limit_event_permissions={"event.orders:read": True, "event.vouchers:read": True},
limit_organizer_permissions={"organizer.giftcards:read": True}
)
team2.limit_events.add(event1)
team2.members.add(user2)
s2 = ScheduledEventExport.objects.create(
event=event1,
owner=user2,
export_identifier="dummy_vouchers",
mail_subject="Test",
mail_template="Test",
schedule_rrule="DTSTART:20230118T000000\nRRULE:FREQ=DAILY;INTERVAL=1;WKST=MO",
schedule_rrule_time=datetime.time(2, 30, 0)
)
assert _can_see_but_not_edit_event_export(client, user2, s2)
assert not _can_see_but_not_edit_event_export(client, user1, s2)
# Scenario 6
# User 2 created an export that requires a special permission on organizer level
# user 1 can see it, because they have permission to see scheduled exports, and change it, because they have
# that special permission
team1.limit_event_permissions["event.vouchers:read"] = True
team1.save()
assert _can_see_but_not_edit_event_export(client, user2, s2)
assert _can_see_but_not_edit_event_export(client, user1, s2)

View File

@@ -51,15 +51,14 @@ def gift_card(organizer):
@pytest.fixture
def admin_user(organizer):
u = User.objects.create_user('dummy@dummy.dummy', 'dummy')
admin_team = Team.objects.create(organizer=organizer, can_manage_gift_cards=True, name='Admin team',
can_change_organizer_settings=True)
admin_team = Team.objects.create(organizer=organizer, name='Admin team', all_organizer_permissions=True)
admin_team.members.add(u)
return u
@pytest.fixture
def team2(admin_user, organizer2):
admin_team = Team.objects.create(organizer=organizer2, can_manage_gift_cards=True, name='Admin team')
admin_team = Team.objects.create(organizer=organizer2, name='Admin team', all_organizer_permissions=True)
admin_team.members.add(admin_user)
@@ -213,8 +212,8 @@ def test_typeahead(organizer, admin_user, client, gift_card):
assert d == {"results": [{"id": gift_card.pk, "text": gift_card.secret}], "pagination": {"more": False}}
# Unprivileged user can only do exact match
team.can_manage_gift_cards = False
team.can_manage_reusable_media = True
team.all_organizer_permissions = False
team.limit_organizer_permissions = {"organizer.reusablemedia:write": True, "organizer.reusablemedia:read": True}
team.save()
r = client.get('/control/organizer/dummy/giftcards/select2?query=' + gift_card.secret[0:3])

View File

@@ -57,9 +57,9 @@ class ItemFormTest(SoupTest):
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
)
self.item1 = Item.objects.create(event=self.event1, name="Standard", default_price=0, position=1)
t = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_change_items=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.team = Team.objects.create(organizer=self.orga1, all_event_permissions=True)
self.team.members.add(self.user)
self.team.limit_events.add(self.event1)
self.client.login(email='dummy@dummy.dummy', password='dummy')
@@ -270,6 +270,14 @@ class QuestionsTest(ItemFormTest):
tbl = doc.select('.container-fluid table.table-bordered tbody')[0]
assert tbl.select('tr')[0].select('td')[0].text.strip() == '42'
# Test permission requirement
self.team.all_event_permissions = False
self.team.limit_event_permissions = {}
self.team.save()
doc = self.get_doc('/control/event/%s/%s/questions/%s/' % (self.orga1.slug, self.event1.slug, c.id))
assert not doc.select('.container-fluid table.table-bordered tbody')
assert doc.select('.empty-collection')
def test_set_dependency(self):
with scopes_disabled():
q1 = Question.objects.create(event=self.event1, question="What country are you from?", type="C", required=True)

View File

@@ -47,7 +47,7 @@ class MailSettingPreviewTest(SoupTest):
)
self.locale_event.settings.locales = ['en', 'de-informal']
self.locale_event.save()
t = Team.objects.create(organizer=self.orga1, can_change_items=True, can_change_event_settings=True)
t = Team.objects.create(organizer=self.orga1, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.locale_event)
t.limit_events.add(self.event1)

View File

@@ -35,8 +35,7 @@ def env():
date_from=now(), plugins='pretix.plugins.banktransfer,pretix.plugins.paypal'
)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=event.organizer, can_view_orders=True, can_change_orders=True,
can_change_vouchers=True)
t = Team.objects.create(organizer=event.organizer, all_event_permissions=True)
t.members.add(user)
t.limit_events.add(event)
return event, user

View File

@@ -67,7 +67,7 @@ def env():
)
event.settings.set('ticketoutput_testdummy__enabled', True)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True, can_manage_customers=True)
t = Team.objects.create(organizer=o, all_event_permissions=True, all_organizer_permissions=True)
t.members.add(user)
t.limit_events.add(event)
o = Order.objects.create(
@@ -1422,7 +1422,7 @@ class OrderChangeTests(SoupTest):
self.quota.items.add(self.ticket)
self.quota.items.add(self.shirt)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True)
t = Team.objects.create(organizer=o, all_event_permissions=True)
t.members.add(user)
t.limit_events.add(self.event)
self.client.login(email='dummy@dummy.dummy', password='dummy')

View File

@@ -56,7 +56,7 @@ def env():
)
event.settings.set('ticketoutput_testdummy__enabled', True)
user = User.objects.create_user('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True, can_manage_customers=True)
t = Team.objects.create(organizer=o, all_event_permissions=True)
t.members.add(user)
t.limit_events.add(event)
ticket = Item.objects.create(event=event, name='Early-bird ticket',

View File

@@ -52,8 +52,7 @@ class OrganizerTest(SoupTest):
plugins='pretix.plugins.banktransfer,tests.testdummy'
)
t = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
can_change_items=True, can_change_organizer_settings=True)
t = Team.objects.create(organizer=self.orga1, all_organizer_permissions=True, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event1)

View File

@@ -44,7 +44,7 @@ from pretix.base.models import Event, Order, Organizer, Team, User
@pytest.fixture
def env():
o = Organizer.objects.create(name='Dummy', slug='dummy')
o = Organizer.objects.create(name='Dummy', slug='dummy', plugins='pretix.plugins.banktransfer')
event = Event.objects.create(
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
@@ -292,7 +292,7 @@ def test_wrong_event(perf_patch, client, env, url):
organizer=env[2], name='Dummy', slug='dummy2',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
t = Team.objects.create(pk=2, organizer=env[2], can_change_event_settings=True)
t = Team.objects.create(pk=2, organizer=env[2], all_event_permissions=True)
t.members.add(env[1])
t.limit_events.add(event2)
@@ -307,115 +307,114 @@ HTTP_POST = "post"
HTTP_GET = "get"
event_permission_urls = [
("can_change_event_settings", "live/", 200, HTTP_GET),
("can_change_event_settings", "delete/", 200, HTTP_GET),
("can_change_event_settings", "dangerzone/", 200, HTTP_GET),
("can_change_event_settings", "settings/", 200, HTTP_GET),
("can_change_event_settings", "settings/plugins", 200, HTTP_GET),
("can_change_event_settings", "settings/payment", 200, HTTP_GET),
("can_change_event_settings", "settings/tickets", 200, HTTP_GET),
("can_change_event_settings", "settings/email", 200, HTTP_GET),
("can_change_event_settings", "settings/email/setup", 200, HTTP_GET),
("can_change_event_settings", "settings/cancel", 200, HTTP_GET),
("can_change_event_settings", "settings/invoice", 200, HTTP_GET),
("can_change_event_settings", "settings/widget", 200, HTTP_GET),
("can_change_event_settings", "settings/invoice/preview", 200, HTTP_GET),
("can_change_event_settings", "settings/tax/", 200, HTTP_GET),
("can_change_event_settings", "settings/tax/1/", 404, HTTP_GET),
("can_change_event_settings", "settings/tax/add", 200, HTTP_GET),
("can_change_event_settings", "settings/tax/1/delete", 404, HTTP_GET),
("can_change_event_settings", "settings/tax/1/default", 404, HTTP_POST),
("can_change_event_settings", "comment/", 405, HTTP_GET),
# Lists are currently not access-controlled
# ("can_change_items", "items/", 200),
("can_change_items", "items/add", 200, HTTP_GET),
("can_change_items", "items/1/up", 404, HTTP_POST),
("can_change_items", "items/1/down", 404, HTTP_POST),
("can_change_items", "items/reorder/2/", 400, HTTP_POST),
("can_change_items", "items/1/delete", 404, HTTP_GET),
# ("can_change_items", "categories/", 200),
("event.settings.general:write", "live/", 200, HTTP_GET),
("event.settings.general:write", "delete/", 200, HTTP_GET),
("event.settings.general:write", "dangerzone/", 200, HTTP_GET),
("event.settings.general:write", "settings/", 200, HTTP_GET),
# ("event.settings.payment:write", "settings/payment", 200, HTTP_GET), GET allowed also with other permissions
("event.settings.payment:write", "settings/payment", 200, HTTP_POST),
("event.settings.payment:write", "settings/payment/banktransfer", 200, HTTP_GET),
("event.settings.general:write", "settings/tickets", 200, HTTP_GET),
("event.settings.general:write", "settings/email", 200, HTTP_GET),
("event.settings.general:write", "settings/email/setup", 200, HTTP_GET),
("event.settings.general:write", "settings/cancel", 200, HTTP_GET),
("event.settings.general:write", "settings/widget", 200, HTTP_GET),
("event.settings.invoicing:write", "settings/invoice", 200, HTTP_GET),
("event.settings.invoicing:write", "settings/invoice/preview", 200, HTTP_GET),
("event.settings.tax:write", "settings/tax/", 200, HTTP_GET),
("event.settings.tax:write", "settings/tax/1/", 404, HTTP_GET),
("event.settings.tax:write", "settings/tax/add", 200, HTTP_GET),
("event.settings.tax:write", "settings/tax/1/delete", 404, HTTP_GET),
("event.settings.tax:write", "settings/tax/1/default", 404, HTTP_POST),
("event.settings.general:write", "comment/", 405, HTTP_GET),
(None, "items/", 200, HTTP_GET),
("event.items:write", "items/add", 200, HTTP_GET),
("event.items:write", "items/1/up", 404, HTTP_POST),
("event.items:write", "items/1/down", 404, HTTP_POST),
("event.items:write", "items/reorder/2/", 400, HTTP_POST),
("event.items:write", "items/1/delete", 404, HTTP_GET),
(None, "categories/", 200, HTTP_GET),
# We don't have to create categories and similar objects
# for testing this, it is enough to test that a 404 error
# is returned instead of a 403 one.
("can_change_items", "categories/2/", 404, HTTP_GET),
("can_change_items", "categories/2/delete", 404, HTTP_GET),
("can_change_items", "categories/2/up", 404, HTTP_POST),
("can_change_items", "categories/2/down", 404, HTTP_POST),
("can_change_items", "categories/reorder", 400, HTTP_POST),
("can_change_items", "categories/add", 200, HTTP_GET),
# ("can_change_items", "questions/", 200, HTTP_GET),
("can_change_items", "questions/2/", 404, HTTP_GET),
("can_change_items", "questions/2/delete", 404, HTTP_GET),
("can_change_items", "questions/reorder", 400, HTTP_POST),
("can_change_items", "questions/add", 200, HTTP_GET),
# ("can_change_items", "quotas/", 200, HTTP_GET),
("can_change_items", "quotas/2/change", 404, HTTP_GET),
("can_change_items", "quotas/2/delete", 404, HTTP_GET),
("can_change_items", "quotas/add", 200, HTTP_GET),
# ("can_change_items", "discounts/", 200),
# We don't have to create categories and similar objects
# for testing this, it is enough to test that a 404 error
# is returned instead of a 403 one.
("can_change_items", "discounts/2/", 404, HTTP_GET),
("can_change_items", "discounts/2/delete", 404, HTTP_GET),
("can_change_items", "discounts/2/up", 404, HTTP_POST),
("can_change_items", "discounts/2/down", 404, HTTP_POST),
("can_change_items", "discounts/reorder", 400, HTTP_POST),
("can_change_items", "discounts/add", 200, HTTP_GET),
("can_change_event_settings", "subevents/", 200, HTTP_GET),
("can_change_event_settings", "subevents/2/", 404, HTTP_GET),
("can_change_event_settings", "subevents/2/delete", 404, HTTP_GET),
("can_change_event_settings", "subevents/add", 200, HTTP_GET),
("can_view_orders", "orders/overview/", 200, HTTP_GET),
("can_view_orders", "orders/export/", 200, HTTP_GET),
("can_view_orders", "orders/export/do", 302, HTTP_POST),
("can_view_orders", "orders/", 200, HTTP_GET),
("can_view_orders", "orders/FOO/", 200, HTTP_GET),
("can_change_orders", "orders/FOO/extend", 200, HTTP_GET),
("can_change_orders", "orders/FOO/reactivate", 302, HTTP_GET),
("can_change_orders", "orders/FOO/contact", 200, HTTP_GET),
("can_change_orders", "orders/FOO/transition", 405, HTTP_GET),
("can_change_orders", "orders/FOO/checkvatid", 405, HTTP_GET),
("can_change_orders", "orders/FOO/resend", 405, HTTP_GET),
("can_change_orders", "orders/FOO/invoice", 405, HTTP_GET),
("can_change_orders", "orders/FOO/change", 200, HTTP_GET),
("can_change_orders", "orders/FOO/approve", 200, HTTP_GET),
("can_change_orders", "orders/FOO/deny", 200, HTTP_GET),
("can_change_orders", "orders/FOO/delete", 302, HTTP_GET),
("can_change_orders", "orders/FOO/comment", 405, HTTP_GET),
("can_change_orders", "orders/FOO/locale", 200, HTTP_GET),
("can_change_orders", "orders/FOO/sendmail", 200, HTTP_GET),
("can_change_orders", "orders/FOO/1/sendmail", 404, HTTP_GET),
("can_change_orders", "orders/import/", 200, HTTP_GET),
("can_change_orders", "orders/import/0ab7b081-92d3-4480-82de-2f8b056fd32f/", 404, HTTP_GET),
("can_view_orders", "orders/FOO/answer/5/", 404, HTTP_GET),
("can_change_orders", "cancel/", 200, HTTP_GET),
("can_change_vouchers", "vouchers/add", 200, HTTP_GET),
("can_change_vouchers", "vouchers/bulk_add", 200, HTTP_GET),
("can_view_vouchers", "vouchers/", 200, HTTP_GET),
("can_view_vouchers", "vouchers/tags/", 200, HTTP_GET),
("can_view_vouchers", "vouchers/1234/", 404, HTTP_GET),
("can_change_vouchers", "vouchers/1234/", 404, HTTP_POST),
("can_change_vouchers", "vouchers/1234/delete", 404, HTTP_GET),
("can_view_orders", "waitinglist/", 200, HTTP_GET),
("can_change_orders", "waitinglist/auto_assign", 405, HTTP_GET),
("can_change_orders", "waitinglist/action", 405, HTTP_GET),
("can_view_orders", "checkins/", 200, HTTP_GET),
("can_view_orders", "checkinlists/", 200, HTTP_GET),
("can_view_orders", "checkinlists/1/", 404, HTTP_GET),
("can_change_orders", "checkinlists/1/bulk_action", 404, HTTP_POST),
("can_checkin_orders", "checkinlists/1/bulk_action", 404, HTTP_POST),
("can_change_event_settings", "checkinlists/add", 200, HTTP_GET),
("can_change_event_settings", "checkinlists/1/change", 404, HTTP_GET),
("can_change_event_settings", "checkinlists/1/delete", 404, HTTP_GET),
("event.items:write", "categories/2/", 404, HTTP_GET),
("event.items:write", "categories/2/delete", 404, HTTP_GET),
("event.items:write", "categories/2/up", 404, HTTP_POST),
("event.items:write", "categories/2/down", 404, HTTP_POST),
("event.items:write", "categories/reorder", 400, HTTP_POST),
("event.items:write", "categories/add", 200, HTTP_GET),
(None, "questions/", 200, HTTP_GET),
(None, "questions/2/", 404, HTTP_GET),
("event.items:write", "questions/2/delete", 404, HTTP_GET),
("event.items:write", "questions/reorder", 400, HTTP_POST),
("event.items:write", "questions/add", 200, HTTP_GET),
(None, "quotas/", 200, HTTP_GET),
("event.items:write", "quotas/2/change", 404, HTTP_GET),
("event.items:write", "quotas/2/delete", 404, HTTP_GET),
("event.items:write", "quotas/add", 200, HTTP_GET),
(None, "discounts/", 200, HTTP_GET),
("event.items:write", "discounts/2/", 404, HTTP_GET),
("event.items:write", "discounts/2/delete", 404, HTTP_GET),
("event.items:write", "discounts/2/up", 404, HTTP_POST),
("event.items:write", "discounts/2/down", 404, HTTP_POST),
("event.items:write", "discounts/reorder", 400, HTTP_POST),
("event.items:write", "discounts/add", 200, HTTP_GET),
(None, "subevents/", 200, HTTP_GET),
("event.subevents:write", "subevents/2/", 404, HTTP_GET),
("event.subevents:write", "subevents/2/", 404, HTTP_POST),
("event.subevents:write", "subevents/2/delete", 404, HTTP_GET),
("event.subevents:write", "subevents/add", 200, HTTP_GET),
("event.subevents:write", "subevents/bulk_add", 200, HTTP_GET),
("event.subevents:write", "subevents/bulk_action", 302, HTTP_POST),
("event.subevents:write", "subevents/bulk_edit", 404, HTTP_POST),
("event.orders:read", "orders/overview/", 200, HTTP_GET),
("event.orders:read", "orders/", 200, HTTP_GET),
("event.orders:read", "orders/FOO/", 200, HTTP_GET),
("event.orders:write", "orders/FOO/extend", 200, HTTP_GET),
("event.orders:write", "orders/FOO/reactivate", 302, HTTP_GET),
("event.orders:write", "orders/FOO/contact", 200, HTTP_GET),
("event.orders:write", "orders/FOO/transition", 405, HTTP_GET),
("event.orders:write", "orders/FOO/checkvatid", 405, HTTP_GET),
("event.orders:write", "orders/FOO/resend", 405, HTTP_GET),
("event.orders:write", "orders/FOO/invoice", 405, HTTP_GET),
("event.orders:write", "orders/FOO/change", 200, HTTP_GET),
("event.orders:write", "orders/FOO/approve", 200, HTTP_GET),
("event.orders:write", "orders/FOO/deny", 200, HTTP_GET),
("event.orders:write", "orders/FOO/delete", 302, HTTP_GET),
("event.orders:write", "orders/FOO/comment", 405, HTTP_GET),
("event.orders:write", "orders/FOO/locale", 200, HTTP_GET),
("event.orders:write", "orders/FOO/sendmail", 200, HTTP_GET),
("event.orders:write", "orders/FOO/1/sendmail", 404, HTTP_GET),
("event.orders:write", "orders/import/", 200, HTTP_GET),
("event.orders:write", "orders/import/0ab7b081-92d3-4480-82de-2f8b056fd32f/", 404, HTTP_GET),
("event.orders:read", "orders/FOO/answer/5/", 404, HTTP_GET),
("event:cancel", "cancel/", 200, HTTP_GET),
("event.vouchers:write", "vouchers/add", 200, HTTP_GET),
("event.vouchers:write", "vouchers/bulk_add", 200, HTTP_GET),
("event.vouchers:read", "vouchers/", 200, HTTP_GET),
("event.vouchers:read", "vouchers/tags/", 200, HTTP_GET),
("event.vouchers:read", "vouchers/1234/", 404, HTTP_GET),
("event.vouchers:write", "vouchers/1234/", 404, HTTP_POST),
("event.vouchers:write", "vouchers/1234/delete", 404, HTTP_GET),
("event.orders:read", "waitinglist/", 200, HTTP_GET),
("event.orders:write", "waitinglist/auto_assign", 405, HTTP_GET),
("event.orders:write", "waitinglist/action", 405, HTTP_GET),
("event.orders:read", "checkins/", 200, HTTP_GET),
("event.orders:read", "checkinlists/", 200, HTTP_GET),
("event.orders:read", "checkinlists/1/", 404, HTTP_GET),
("event.orders:write", "checkinlists/1/bulk_action", 404, HTTP_POST),
("event.orders:checkin", "checkinlists/1/bulk_action", 404, HTTP_POST),
("event.settings.general:write", "checkinlists/add", 200, HTTP_GET),
("event.settings.general:write", "checkinlists/1/change", 404, HTTP_GET),
("event.settings.general:write", "checkinlists/1/delete", 404, HTTP_GET),
# bank transfer
("can_change_orders", "banktransfer/import/", 200, HTTP_GET),
("can_change_orders", "banktransfer/job/1/", 404, HTTP_GET),
("can_change_orders", "banktransfer/action/", 200, HTTP_GET),
("can_change_orders", "banktransfer/refunds/", 200, HTTP_GET),
("can_change_orders", "banktransfer/export/1/", 404, HTTP_GET),
("can_change_orders", "banktransfer/sepa-export/1/", 404, HTTP_GET),
("event.orders:write", "banktransfer/import/", 200, HTTP_GET),
("event.orders:write", "banktransfer/job/1/", 404, HTTP_GET),
("event.orders:write", "banktransfer/action/", 200, HTTP_GET),
("event.orders:write", "banktransfer/refunds/", 200, HTTP_GET),
("event.orders:write", "banktransfer/export/1/", 404, HTTP_GET),
("event.orders:write", "banktransfer/sepa-export/1/", 404, HTTP_GET),
]
@@ -425,7 +424,10 @@ def test_wrong_event_permission(perf_patch, client, env, perm, url, code, http_m
t = Team(
pk=2, organizer=env[2], all_events=True
)
setattr(t, perm, False)
if not perm:
pytest.skip()
t.all_event_permissions = False
t.limit_event_permissions.pop(perm, None)
t.save()
t.members.add(env[1])
client.login(email='dummy@dummy.dummy', password='dummy')
@@ -443,7 +445,7 @@ def test_limited_event_permission_for_other_event(perf_patch, client, env, perm,
organizer=env[2], name='Dummy', slug='dummy2',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
t = Team.objects.create(pk=2, organizer=env[2], can_change_event_settings=True)
t = Team.objects.create(pk=2, organizer=env[2], all_event_permissions=True)
t.members.add(env[1])
t.limit_events.add(event2)
@@ -460,14 +462,16 @@ def test_current_permission(client, env):
t = Team(
pk=2, organizer=env[2], all_events=True
)
setattr(t, 'can_change_event_settings', True)
setattr(t, 'event.settings.general:write', True)
t.all_event_permissions = False
t.limit_event_permissions['event.settings.general:write'] = True
t.save()
t.members.add(env[1])
client.login(email='dummy@dummy.dummy', password='dummy')
response = client.get('/control/event/dummy/dummy/settings/')
assert response.status_code == 200
setattr(t, 'can_change_event_settings', False)
t.limit_event_permissions.pop('event.settings.general:write', None)
t.save()
response = client.get('/control/event/dummy/dummy/settings/')
assert response.status_code == 403
@@ -477,7 +481,8 @@ def test_current_permission(client, env):
@pytest.mark.parametrize("perm,url,code,http_method", event_permission_urls)
def test_correct_event_permission_all_events(perf_patch, client, env, perm, url, code, http_method):
t = Team(pk=2, organizer=env[2], all_events=True)
setattr(t, perm, True)
t.all_event_permissions = False
t.limit_event_permissions[perm] = True
t.save()
t.members.add(env[1])
client.login(email='dummy@dummy.dummy', password='dummy')
@@ -495,7 +500,8 @@ def test_correct_event_permission_all_events(perf_patch, client, env, perm, url,
@pytest.mark.parametrize("perm,url,code,http_method", event_permission_urls)
def test_correct_event_permission_limited(perf_patch, client, env, perm, url, code, http_method):
t = Team(pk=2, organizer=env[2])
setattr(t, perm, True)
t.all_event_permissions = False
t.limit_event_permissions[perm] = True
t.save()
t.members.add(env[1])
t.limit_events.add(env[0])
@@ -521,77 +527,80 @@ def test_wrong_organizer(perf_patch, client, env, url):
organizer_permission_urls = [
("can_change_teams", "organizer/dummy/teams", 200),
("can_change_teams", "organizer/dummy/team/add", 200),
("can_change_teams", "organizer/dummy/team/1/", 200),
("can_change_teams", "organizer/dummy/team/1/edit", 200),
("can_change_teams", "organizer/dummy/team/1/delete", 200),
("can_change_organizer_settings", "organizer/dummy/edit", 200),
("can_change_organizer_settings", "organizer/dummy/settings/plugins", 200),
("can_change_organizer_settings", "organizer/dummy/settings/plugins/pretix.plugins.sendmail/events", 200),
("can_change_organizer_settings", "organizer/dummy/settings/email", 200),
("can_change_organizer_settings", "organizer/dummy/settings/email/setup", 200),
("can_change_organizer_settings", "organizer/dummy/outgoingmails", 200),
("can_change_organizer_settings", "organizer/dummy/outgoingmail/1/", 404),
("can_change_organizer_settings", "organizer/dummy/outgoingmail/bulk_action", 405),
("can_change_organizer_settings", "organizer/dummy/devices", 200),
("can_change_organizer_settings", "organizer/dummy/devices/select2", 200),
("can_change_organizer_settings", "organizer/dummy/device/add", 200),
("can_change_organizer_settings", "organizer/dummy/device/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/device/1/connect", 404),
("can_change_organizer_settings", "organizer/dummy/device/1/revoke", 404),
("can_change_organizer_settings", "organizer/dummy/gates", 200),
("can_change_organizer_settings", "organizer/dummy/gates/select2", 200),
("can_change_organizer_settings", "organizer/dummy/gate/add", 200),
("can_change_organizer_settings", "organizer/dummy/gate/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/gate/1/delete", 404),
("can_change_organizer_settings", "organizer/dummy/properties", 200),
("can_change_organizer_settings", "organizer/dummy/property/add", 200),
("can_change_organizer_settings", "organizer/dummy/property/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/property/1/delete", 404),
("can_change_organizer_settings", "organizer/dummy/channels", 200),
("can_change_organizer_settings", "organizer/dummy/channel/add", 200),
("can_change_organizer_settings", "organizer/dummy/channel/web/edit", 200),
("can_change_organizer_settings", "organizer/dummy/channel/web/delete", 200),
("can_change_organizer_settings", "organizer/dummy/membershiptypes", 200),
("can_change_organizer_settings", "organizer/dummy/membershiptype/add", 200),
("can_change_organizer_settings", "organizer/dummy/membershiptype/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/membershiptype/1/delete", 404),
("can_change_organizer_settings", "organizer/dummy/ssoproviders", 200),
("can_change_organizer_settings", "organizer/dummy/ssoprovider/add", 200),
("can_change_organizer_settings", "organizer/dummy/ssoprovider/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/ssoprovider/1/delete", 404),
("can_manage_customers", "organizer/dummy/customers", 200),
("can_manage_customers", "organizer/dummy/customer/ABC/edit", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/anonymize", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/membership/add", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/membership/1/edit", 404),
("can_manage_customers", "organizer/dummy/customer/ABC/", 404),
("can_manage_reusable_media", "organizer/dummy/reusable_media", 200),
("can_manage_reusable_media", "organizer/dummy/reusable_media/1/edit", 404),
("can_manage_reusable_media", "organizer/dummy/reusable_media/1/", 404),
("can_manage_gift_cards", "organizer/dummy/giftcards", 200),
("can_manage_gift_cards", "organizer/dummy/giftcard/add", 200),
("can_manage_gift_cards", "organizer/dummy/giftcard/1/", 404),
("can_manage_gift_cards", "organizer/dummy/giftcard/1/edit", 404),
("can_change_organizer_settings", "organizer/dummy/giftcards/acceptance", 200),
("can_change_organizer_settings", "organizer/dummy/giftcards/acceptance/invite", 200),
("organizer.teams:write", "organizer/dummy/teams", 200),
("organizer.teams:write", "organizer/dummy/team/add", 200),
("organizer.teams:write", "organizer/dummy/team/1/", 200),
("organizer.teams:write", "organizer/dummy/team/1/edit", 200),
("organizer.teams:write", "organizer/dummy/team/1/delete", 200),
("organizer.settings.general:write", "organizer/dummy/edit", 200),
("organizer.settings.general:write", "organizer/dummy/settings/plugins", 200),
("organizer.settings.general:write", "organizer/dummy/settings/plugins/pretix.plugins.sendmail/events", 200),
("organizer.settings.general:write", "organizer/dummy/settings/email", 200),
("organizer.settings.general:write", "organizer/dummy/settings/email/setup", 200),
("organizer.outgoingmails:read", "organizer/dummy/outgoingmails", 200),
("organizer.outgoingmails:read", "organizer/dummy/outgoingmail/1/", 404),
("organizer.outgoingmails:read", "organizer/dummy/outgoingmail/bulk_action", 405),
("organizer.devices:read", "organizer/dummy/devices", 200),
("organizer.devices:read", "organizer/dummy/devices/select2", 200),
("organizer.devices:write", "organizer/dummy/device/add", 200),
("organizer.devices:write", "organizer/dummy/device/1/edit", 404),
("organizer.devices:write", "organizer/dummy/device/1/connect", 404),
("organizer.devices:write", "organizer/dummy/device/1/revoke", 404),
("organizer.devices:read", "organizer/dummy/gates", 200),
("organizer.devices:read", "organizer/dummy/gates/select2", 200),
("organizer.devices:write", "organizer/dummy/gate/add", 200),
("organizer.devices:write", "organizer/dummy/gate/1/edit", 404),
("organizer.devices:write", "organizer/dummy/gate/1/delete", 404),
("organizer.settings.general:write", "organizer/dummy/properties", 200),
("organizer.settings.general:write", "organizer/dummy/property/add", 200),
("organizer.settings.general:write", "organizer/dummy/property/1/edit", 404),
("organizer.settings.general:write", "organizer/dummy/property/1/delete", 404),
("organizer.settings.general:write", "organizer/dummy/channels", 200),
("organizer.settings.general:write", "organizer/dummy/channel/add", 200),
("organizer.settings.general:write", "organizer/dummy/channel/web/edit", 200),
("organizer.settings.general:write", "organizer/dummy/channel/web/delete", 200),
("organizer.settings.general:write", "organizer/dummy/membershiptypes", 200),
("organizer.settings.general:write", "organizer/dummy/membershiptype/add", 200),
("organizer.settings.general:write", "organizer/dummy/membershiptype/1/edit", 404),
("organizer.settings.general:write", "organizer/dummy/membershiptype/1/delete", 404),
("organizer.settings.general:write", "organizer/dummy/ssoproviders", 200),
("organizer.settings.general:write", "organizer/dummy/ssoprovider/add", 200),
("organizer.settings.general:write", "organizer/dummy/ssoprovider/1/edit", 404),
("organizer.settings.general:write", "organizer/dummy/ssoprovider/1/delete", 404),
("organizer.customers:read", "organizer/dummy/customers", 200),
("organizer.customers:write", "organizer/dummy/customer/ABC/edit", 404),
("organizer.customers:write", "organizer/dummy/customer/ABC/anonymize", 404),
("organizer.customers:write", "organizer/dummy/customer/ABC/membership/add", 404),
("organizer.customers:write", "organizer/dummy/customer/ABC/membership/1/edit", 404),
("organizer.customers:read", "organizer/dummy/customer/ABC/", 404),
("organizer.reusablemedia:read", "organizer/dummy/reusable_media", 200),
("organizer.reusablemedia:write", "organizer/dummy/reusable_media/1/edit", 404),
("organizer.reusablemedia:read", "organizer/dummy/reusable_media/1/", 404),
("organizer.giftcards:read", "organizer/dummy/giftcards", 200),
("organizer.giftcards:write", "organizer/dummy/giftcard/add", 200),
("organizer.giftcards:read", "organizer/dummy/giftcard/1/", 404),
("organizer.giftcards:write", "organizer/dummy/giftcard/1/edit", 404),
("organizer.settings.general:write", "organizer/dummy/giftcards/acceptance", 200),
("organizer.settings.general:write", "organizer/dummy/giftcards/acceptance/invite", 200),
# bank transfer
("can_change_orders", "organizer/dummy/banktransfer/import/", 200),
("can_change_orders", "organizer/dummy/banktransfer/job/1/", 404),
("can_change_orders", "organizer/dummy/banktransfer/action/", 200),
("can_change_orders", "organizer/dummy/banktransfer/refunds/", 200),
("can_change_orders", "organizer/dummy/banktransfer/export/1/", 404),
("can_change_orders", "organizer/dummy/banktransfer/sepa-export/1/", 404),
("event.orders:write", "organizer/dummy/banktransfer/import/", 200),
("event.orders:write", "organizer/dummy/banktransfer/job/1/", 404),
("event.orders:write", "organizer/dummy/banktransfer/action/", 200),
("event.orders:write", "organizer/dummy/banktransfer/refunds/", 200),
("event.orders:write", "organizer/dummy/banktransfer/export/1/", 404),
("event.orders:write", "organizer/dummy/banktransfer/sepa-export/1/", 404),
]
@pytest.mark.django_db
@pytest.mark.parametrize("perm,url,code", organizer_permission_urls)
def test_wrong_organizer_permission(perf_patch, client, env, perm, url, code):
t = Team(pk=2, organizer=env[2])
setattr(t, perm, False)
t = Team(pk=2, organizer=env[2], all_events=True)
t.all_organizer_permissions = False
t.limit_organizer_permissions.pop(perm, None)
t.all_event_permissions = False
t.limit_event_permissions.pop(perm, None)
t.save()
t.members.add(env[1])
client.login(email='dummy@dummy.dummy', password='dummy')
@@ -602,8 +611,14 @@ def test_wrong_organizer_permission(perf_patch, client, env, perm, url, code):
@pytest.mark.django_db
@pytest.mark.parametrize("perm,url,code", organizer_permission_urls)
def test_correct_organizer_permission(perf_patch, client, env, perm, url, code):
t = Team(pk=2, organizer=env[2])
setattr(t, perm, True)
t = Team(pk=2, organizer=env[2], all_events=True)
if perm.startswith("event."):
t.all_organizer_permissions = False
t.all_event_permissions = False
t.limit_event_permissions[perm] = True
else:
t.all_organizer_permissions = False
t.limit_organizer_permissions[perm] = True
t.save()
t.members.add(env[1])
client.login(email='dummy@dummy.dummy', password='dummy')

View File

@@ -51,7 +51,7 @@ def gift_card(organizer):
@pytest.fixture
def admin_user(organizer):
u = User.objects.create_user('dummy@dummy.dummy', 'dummy')
admin_team = Team.objects.create(organizer=organizer, can_manage_reusable_media=True, name='Admin team')
admin_team = Team.objects.create(organizer=organizer, all_organizer_permissions=True, name='Admin team')
admin_team.members.add(u)
return u
@@ -122,7 +122,7 @@ def test_typeahead(organizer, admin_user, client, gift_card):
# Privileged user can search
team.all_events = True
team.can_view_orders = True
team.limit_event_permissions["event.orders:read"] = True
team.save()
r = client.get('/control/organizer/dummy/ticket_select2?query=' + op.secret[0:3])
@@ -140,7 +140,8 @@ def test_typeahead(organizer, admin_user, client, gift_card):
# Unprivileged user can only do exact match
team.all_events = True
team.can_view_orders = False
team.all_event_permissions = False
team.limit_event_permissions = {"event.vouchers:read": True}
team.save()
r = client.get('/control/organizer/dummy/ticket_select2?query=' + op.secret[0:3])
@@ -154,7 +155,7 @@ def test_typeahead(organizer, admin_user, client, gift_card):
assert d == {"results": [{'event': 'Dummy', 'id': op.pk, 'text': 'FOO-1 (Early-bird ticket)'}], "pagination": {"more": False}}
team.all_events = False
team.can_view_orders = True
team.limit_event_permissions["event.orders:read"] = True
team.save()
r = client.get('/control/organizer/dummy/ticket_select2?query=' + op.secret[0:3])

View File

@@ -86,7 +86,7 @@ class OrderSearchTest(SoupTest):
attendee_name_parts={'full_name': "Mark", "_scheme": "full"}
)
self.team = Team.objects.create(organizer=self.orga1, can_view_orders=True)
self.team = Team.objects.create(organizer=self.orga1, limit_event_permissions={"event.orders:read": True})
self.team.members.add(self.user)
self.team.limit_events.add(self.event1)
@@ -98,7 +98,8 @@ class OrderSearchTest(SoupTest):
assert 'DEFFO2' not in resp
def test_team_limit_event_wrong_permission(self):
self.team.can_view_orders = False
self.team.all_event_permissions = False
self.team.limit_event_permissions = {"event.vouchers:read": True}
self.team.save()
resp = self.client.get('/control/search/orders/').content.decode()
assert 'ABCFO1' not in resp
@@ -113,7 +114,8 @@ class OrderSearchTest(SoupTest):
def test_team_all_events_wrong_permission(self):
self.team.all_events = True
self.team.can_view_orders = False
self.team.all_event_permissions = False
self.team.limit_event_permissions = {"event.vouchers:read": True}
self.team.save()
resp = self.client.get('/control/search/orders/').content.decode()
assert 'ABCFO1' not in resp
@@ -270,8 +272,8 @@ class PaymentSearchTest(SoupTest):
info="{test payment order 2}"
)
self.team = Team.objects.create(organizer=self.orga1, can_view_orders=True)
self.team2 = Team.objects.create(organizer=self.orga2, can_view_orders=True)
self.team = Team.objects.create(organizer=self.orga1, limit_event_permissions={"event.orders:read": True})
self.team2 = Team.objects.create(organizer=self.orga2, limit_event_permissions={"event.orders:read": True})
self.team.members.add(self.user)
self.team.limit_events.add(self.event1)
@@ -283,7 +285,8 @@ class PaymentSearchTest(SoupTest):
assert 'DEFFO2' not in resp
def test_team_limit_event_wrong_permission(self):
self.team.can_view_orders = False
self.team.all_event_permissions = False
self.team.limit_event_permissions = {"event.vouchers:read": True}
self.team.save()
resp = self.client.get('/control/search/payments/').content.decode()
assert 'ABCFO1' not in resp
@@ -298,7 +301,8 @@ class PaymentSearchTest(SoupTest):
def test_team_all_events_wrong_permission(self):
self.team.all_events = True
self.team.can_view_orders = False
self.team.all_event_permissions = False
self.team.limit_event_permissions = {"event.vouchers:read": True}
self.team.save()
resp = self.client.get('/control/search/payments/').content.decode()
assert 'ABCFO1' not in resp

View File

@@ -58,8 +58,7 @@ class EventShredderTest(SoupTest):
plugins='pretix.plugins.banktransfer,pretix.plugins.stripe,tests.testdummy'
)
t = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
can_change_items=True, can_change_orders=True)
t = Team.objects.create(organizer=self.orga1, all_organizer_permissions=True, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.order = Order.objects.create(

View File

@@ -45,8 +45,7 @@ class SubEventsTest(SoupTest):
has_subevents=True
)
t = Team.objects.create(organizer=self.orga1, can_create_events=True, can_change_event_settings=True,
can_change_items=True)
t = Team.objects.create(organizer=self.orga1, all_organizer_permissions=True, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.ticket = self.event1.items.create(name='Early-bird ticket',

View File

@@ -41,7 +41,7 @@ class TaxRateFormTest(SoupTest):
organizer=self.orga1, name='30C3', slug='30c3',
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
)
t = Team.objects.create(organizer=self.orga1, can_change_event_settings=True, can_change_items=True)
t = Team.objects.create(organizer=self.orga1, all_organizer_permissions=True, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event1)
self.client.login(email='dummy@dummy.dummy', password='dummy')

View File

@@ -56,7 +56,7 @@ def event(organizer):
@pytest.fixture
def admin_team(organizer):
return Team.objects.create(organizer=organizer, can_change_teams=True, name='Admin team')
return Team.objects.create(organizer=organizer, all_organizer_permissions=True, all_event_permissions=True, name='Admin team')
@pytest.fixture
@@ -216,7 +216,7 @@ def test_team_remove_last_admin(event, admin_user, admin_team, client):
with scopes_disabled():
assert admin_user in admin_team.members.all()
t2.can_change_teams = True
t2.limit_organizer_permissions = {"organizer.teams:write": True}
t2.save()
resp = client.post('/control/organizer/dummy/team/{}/'.format(admin_team.pk), {
'remove-member': admin_user.pk
@@ -229,17 +229,35 @@ def test_team_remove_last_admin(event, admin_user, admin_team, client):
@pytest.mark.django_db
def test_create_team(event, admin_user, admin_team, client):
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/organizer/dummy/team/add', {
r = client.post('/control/organizer/dummy/team/add', {
'name': 'Foo',
'can_create_events': 'on',
'organizer_organizer.events': "create",
'organizer_organizer.settings.general': "EMPTY",
'organizer_organizer.teams': "EMPTY",
'organizer_organizer.giftcards': "EMPTY",
'organizer_organizer.customers': "EMPTY",
'organizer_organizer.reusablemedia': "EMPTY",
'organizer_organizer.devices': "EMPTY",
'organizer_organizer.seatingplans': "EMPTY",
'organizer_organizer.outgoingmails': "EMPTY",
'event_event.settings.general': "write",
'event_event.settings.payment': "EMPTY",
'event_event.settings.tax': "EMPTY",
'event_event.settings.invoicing': "EMPTY",
'event_event.subevents': "EMPTY",
'event_event.items': "EMPTY",
'event_event.orders': "EMPTY",
'event_event.vouchers': "EMPTY",
'event_event': "EMPTY",
'limit_events': str(event.pk),
'can_change_event_settings': 'on'
}, follow=True)
assert 'alert-success' in r.content.decode()
with scopes_disabled():
t = Team.objects.last()
assert t.can_change_event_settings
assert t.can_create_events
assert not t.can_change_organizer_settings
assert not t.all_event_permissions
assert t.limit_event_permissions == {"event.settings.general:write": True}
assert not t.all_organizer_permissions
assert t.limit_organizer_permissions == {"organizer.events:create": True}
assert list(t.limit_events.all()) == [event]
assert list(t.members.all()) == [admin_user]
@@ -247,15 +265,36 @@ def test_create_team(event, admin_user, admin_team, client):
@pytest.mark.django_db
def test_update_team(event, admin_user, admin_team, client):
client.login(email='dummy@dummy.dummy', password='dummy')
client.post('/control/organizer/dummy/team/{}/edit'.format(admin_team.pk), {
r = client.post('/control/organizer/dummy/team/{}/edit'.format(admin_team.pk), {
'name': 'Admin',
'can_change_teams': 'on',
'limit_events': str(event.pk),
'can_change_event_settings': 'on'
'all_event_permissions': 'on',
'all_organizer_permissions': '',
'organizer_organizer.events': "EMPTY",
'organizer_organizer.settings.general': "EMPTY",
'organizer_organizer.teams': "write",
'organizer_organizer.giftcards': "EMPTY",
'organizer_organizer.customers': "EMPTY",
'organizer_organizer.reusablemedia': "EMPTY",
'organizer_organizer.devices': "EMPTY",
'organizer_organizer.seatingplans': "EMPTY",
'organizer_organizer.outgoingmails': "EMPTY",
'event_event.settings.general': "write",
'event_event.settings.payment': "EMPTY",
'event_event.settings.tax': "EMPTY",
'event_event.settings.invoicing': "EMPTY",
'event_event.subevents': "EMPTY",
'event_event.items': "EMPTY",
'event_event.orders': "EMPTY",
'event_event.vouchers': "EMPTY",
'event_event': "EMPTY",
}, follow=True)
assert 'alert-success' in r.content.decode()
admin_team.refresh_from_db()
assert admin_team.can_change_event_settings
assert not admin_team.can_change_organizer_settings
assert admin_team.all_event_permissions
assert admin_team.limit_event_permissions == {}
assert not admin_team.all_organizer_permissions
assert admin_team.limit_organizer_permissions == {"organizer.teams:write": True}
with scopes_disabled():
assert list(admin_team.limit_events.all()) == [event]
@@ -265,7 +304,23 @@ def test_update_last_team_to_be_no_admin(event, admin_user, admin_team, client):
client.login(email='dummy@dummy.dummy', password='dummy')
resp = client.post('/control/organizer/dummy/team/{}/edit'.format(admin_team.pk), {
'name': 'Admin',
'can_change_event_settings': 'on'
'organizer_organizer.events': "write",
'organizer_organizer.settings.general': "EMPTY",
'organizer_organizer.teams': "EMPTY",
'organizer_organizer.giftcards': "EMPTY",
'organizer_organizer.customers': "EMPTY",
'organizer_organizer.reusablemedia': "EMPTY",
'organizer_organizer.devices': "EMPTY",
'organizer_organizer.seatingplans': "EMPTY",
'event_event.settings.general': "write",
'event_event.settings.payment': "EMPTY",
'event_event.settings.tax': "EMPTY",
'event_event.settings.invoicing': "EMPTY",
'event_event.subevents': "EMPTY",
'event_event.items': "EMPTY",
'event_event.orders': "EMPTY",
'event_event.vouchers': "EMPTY",
'event_event': "EMPTY",
}, follow=True)
assert 'alert-danger' in resp.content.decode()

View File

@@ -482,7 +482,7 @@ class UserSettingsNotificationsTest(SoupTest):
organizer=o, name='Dummy', slug='dummy',
date_from=now(), plugins='pretix.plugins.banktransfer'
)
t = o.teams.create(can_change_orders=True, all_events=True)
t = o.teams.create(limit_event_permissions={"event.orders:write": True}, all_events=True)
t.members.add(self.user)
def test_toggle_all(self):

View File

@@ -110,9 +110,8 @@ def logged_in_client(client, event):
user = User.objects.create_superuser('dummy@dummy.dummy', 'dummy')
t = Team.objects.create(
organizer=event.organizer,
all_events=True, can_create_events=True, can_change_teams=True,
can_change_organizer_settings=True, can_change_event_settings=True, can_change_items=True,
can_view_orders=True, can_change_orders=True, can_view_vouchers=True, can_change_vouchers=True
all_event_permissions=True,
all_organizer_permissions=True,
)
t.members.add(user)
client.force_login(user)

View File

@@ -58,7 +58,7 @@ class VoucherFormTest(SoupTestMixin, TransactionTestCase):
organizer=self.orga, name='30C3', slug='30c3',
date_from=datetime.datetime(2013, 12, 26, tzinfo=datetime.timezone.utc),
)
t = Team.objects.create(organizer=self.orga, can_view_vouchers=True, can_change_vouchers=True)
t = Team.objects.create(organizer=self.orga, all_event_permissions=True)
t.members.add(self.user)
t.limit_events.add(self.event)
self.client.login(email='dummy@dummy.dummy', password='dummy')

View File

@@ -66,7 +66,7 @@ def env():
event=event, item=item2, email='valid@example.org', voucher=v
)
t = Team.objects.create(organizer=o, can_view_orders=True, can_change_orders=True)
t = Team.objects.create(organizer=o, all_event_permissions=True)
t.members.add(user)
t.limit_events.add(event)

View File

@@ -63,7 +63,7 @@ def admin_user(admin_team):
@pytest.fixture
def admin_team(organizer):
return Team.objects.create(organizer=organizer, can_change_organizer_settings=True, name='Admin team')
return Team.objects.create(organizer=organizer, all_organizer_permissions=True, name='Admin team')
@pytest.mark.django_db