mirror of
https://github.com/pretix/pretix.git
synced 2026-05-27 18:54:06 +00:00
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:
@@ -48,7 +48,7 @@ def event():
|
||||
|
||||
@pytest.fixture
|
||||
def team(event):
|
||||
return event.organizer.teams.create(all_events=True, can_view_orders=True)
|
||||
return event.organizer.teams.create(all_events=True, all_event_permissions=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -105,7 +105,7 @@ def test_event_fail_invalid_config(event, user):
|
||||
assert s.error_counter == 1
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].subject == "Export failed"
|
||||
assert "Reason: Export type not found." in djmail.outbox[0].body
|
||||
assert "Reason: Export type not found" in djmail.outbox[0].body
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
|
||||
|
||||
@@ -143,7 +143,8 @@ def test_event_fail_user_no_permission(event, user, team):
|
||||
s.error_counter = 0
|
||||
s.save()
|
||||
|
||||
team.can_view_orders = False
|
||||
team.all_event_permissions = False
|
||||
team.limit_event_permissions = {"event.vouchers:read": True}
|
||||
team.save()
|
||||
|
||||
run_scheduled_exports(None)
|
||||
@@ -152,7 +153,7 @@ def test_event_fail_user_no_permission(event, user, team):
|
||||
assert s.error_counter == 1
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].subject == "Export failed"
|
||||
assert "Reason: Permission denied." in djmail.outbox[0].body
|
||||
assert "Reason: Export type not found or permission denied." in djmail.outbox[0].body
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
|
||||
|
||||
@@ -235,7 +236,7 @@ def test_organizer_fail_invalid_config(event, user):
|
||||
assert s.error_counter == 1
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].subject == "Export failed"
|
||||
assert "Reason: Export type not found." in djmail.outbox[0].body
|
||||
assert "Reason: Export type not found" in djmail.outbox[0].body
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
|
||||
|
||||
@@ -273,7 +274,8 @@ def test_organizer_fail_user_does_not_have_specific_permission(event, user, team
|
||||
s.error_counter = 0
|
||||
s.save()
|
||||
|
||||
team.can_manage_customers = False
|
||||
team.all_event_permissions = False
|
||||
team.limit_event_permissions = {"organizer.giftcards:write": True}
|
||||
team.save()
|
||||
|
||||
run_scheduled_exports(None)
|
||||
@@ -282,7 +284,7 @@ def test_organizer_fail_user_does_not_have_specific_permission(event, user, team
|
||||
assert s.error_counter == 1
|
||||
assert len(djmail.outbox) == 1
|
||||
assert djmail.outbox[0].subject == "Export failed"
|
||||
assert "Reason: Permission denied." in djmail.outbox[0].body
|
||||
assert "Reason: Export type not found or permission denied." in djmail.outbox[0].body
|
||||
assert djmail.outbox[0].to == [user.email]
|
||||
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ def order(event):
|
||||
|
||||
@pytest.fixture
|
||||
def team(event):
|
||||
return event.organizer.teams.create(all_events=True, can_view_orders=True)
|
||||
return event.organizer.teams.create(all_events=True, all_event_permissions=True)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -142,7 +142,8 @@ def test_notification_ignore_same_user(event, order, user, monkeypatch_on_commit
|
||||
@pytest.mark.django_db
|
||||
def test_notification_ignore_insufficient_permissions(event, order, user, team, monkeypatch_on_commit):
|
||||
djmail.outbox = []
|
||||
team.can_view_orders = False
|
||||
team.all_event_permissions = False
|
||||
team.limit_event_permissions = {"event.vouchers:read": True}
|
||||
team.save()
|
||||
user.notification_settings.create(
|
||||
method='mail', event=event, action_type='pretix.event.order.paid', enabled=True
|
||||
|
||||
@@ -66,13 +66,6 @@ def admin_request(admin, client):
|
||||
return r
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_invalid_permission(event, user):
|
||||
team = Team.objects.create(organizer=event.organizer)
|
||||
with pytest.raises(ValueError):
|
||||
team.has_permission('FOOOOOOBAR')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_any_event_permission_limited(event, user):
|
||||
user._teamcache = {}
|
||||
@@ -117,59 +110,59 @@ def test_any_event_permission_all(event, user):
|
||||
@pytest.mark.django_db
|
||||
def test_specific_event_permission_limited(event, user):
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team = Team.objects.create(organizer=event.organizer, can_change_orders=True)
|
||||
team = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.orders:write": True})
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team.members.add(user)
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team.limit_events.add(event)
|
||||
user._teamcache = {}
|
||||
assert user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_event_settings')
|
||||
assert user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.settings.general:write')
|
||||
|
||||
assert user.has_event_permission(event.organizer, event, ('can_change_orders', 'can_change_event_settings'))
|
||||
assert not user.has_event_permission(event.organizer, event, ('can_change_teams', 'can_change_event_settings'))
|
||||
assert user.has_event_permission(event.organizer, event, ('event.orders:write', 'event.settings.general:write'))
|
||||
assert not user.has_event_permission(event.organizer, event, ('event.items:write', 'event.settings.general:write'))
|
||||
|
||||
team.can_change_orders = False
|
||||
team.limit_event_permissions = {}
|
||||
team.save()
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_specific_event_permission_all(event, user):
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team = Team.objects.create(organizer=event.organizer, can_change_orders=True)
|
||||
team = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.orders:write": True})
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team.members.add(user)
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team.all_events = True
|
||||
team.save()
|
||||
user._teamcache = {}
|
||||
assert user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
team.can_change_orders = False
|
||||
team.limit_event_permissions = {}
|
||||
team.save()
|
||||
user._teamcache = {}
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_event_permissions_multiple_teams(event, user):
|
||||
team1 = Team.objects.create(organizer=event.organizer, can_change_orders=True, all_events=True)
|
||||
team2 = Team.objects.create(organizer=event.organizer, can_change_vouchers=True)
|
||||
team3 = Team.objects.create(organizer=event.organizer, can_change_event_settings=True)
|
||||
team1 = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.orders:write": True}, all_events=True)
|
||||
team2 = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.vouchers:write": True})
|
||||
team3 = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.settings.general:write": True})
|
||||
event2 = Event.objects.create(
|
||||
organizer=event.organizer, name='Dummy', slug='dummy2',
|
||||
date_from=now()
|
||||
@@ -180,12 +173,17 @@ def test_event_permissions_multiple_teams(event, user):
|
||||
team2.limit_events.add(event)
|
||||
team3.limit_events.add(event2)
|
||||
|
||||
assert user.has_event_permission(event.organizer, event, 'can_change_orders')
|
||||
assert user.has_event_permission(event.organizer, event, 'can_change_vouchers')
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_change_event_settings')
|
||||
assert user.get_event_permission_set(event.organizer, event) == {'can_change_orders', 'can_change_vouchers'}
|
||||
assert user.get_event_permission_set(event.organizer, event2) == {'can_change_orders', 'can_change_event_settings',
|
||||
'can_change_settings'}
|
||||
assert user.has_event_permission(event.organizer, event, 'event.orders:write')
|
||||
assert user.has_event_permission(event.organizer, event, 'event.vouchers:write')
|
||||
assert not user.has_event_permission(event.organizer, event, 'event.settings.general:write')
|
||||
assert user.get_event_permission_set(event.organizer, event) == {
|
||||
'event.orders:write', 'event.vouchers:write',
|
||||
'can_change_orders', 'can_change_vouchers',
|
||||
}
|
||||
assert user.get_event_permission_set(event.organizer, event2) == {
|
||||
'event.orders:write', 'event.settings.general:write', 'event.settings.general:write',
|
||||
'can_change_orders', 'can_change_event_settings', 'can_change_settings',
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@@ -205,41 +203,47 @@ def test_any_organizer_permission(event, user):
|
||||
@pytest.mark.django_db
|
||||
def test_specific_organizer_permission(event, user):
|
||||
user._teamcache = {}
|
||||
assert not user.has_organizer_permission(event.organizer, 'can_create_events')
|
||||
assert not user.has_organizer_permission(event.organizer, 'organizer.events:create')
|
||||
|
||||
team = Team.objects.create(organizer=event.organizer, can_create_events=True)
|
||||
team = Team.objects.create(organizer=event.organizer, limit_organizer_permissions={"organizer.events:create": True})
|
||||
user._teamcache = {}
|
||||
assert not user.has_organizer_permission(event.organizer, 'can_create_events')
|
||||
assert not user.has_organizer_permission(event.organizer, 'organizer.events:create')
|
||||
|
||||
team.members.add(user)
|
||||
user._teamcache = {}
|
||||
assert user.has_organizer_permission(event.organizer, 'can_create_events')
|
||||
assert user.has_organizer_permission(event.organizer, ('can_create_events', 'can_change_organizer_settings'))
|
||||
assert user.has_organizer_permission(event.organizer, 'organizer.events:create')
|
||||
assert user.has_organizer_permission(event.organizer, ('organizer.events:create', 'organizer.settings.general:write'))
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_organizer_permissions_multiple_teams(event, user):
|
||||
team1 = Team.objects.create(organizer=event.organizer, can_change_organizer_settings=True)
|
||||
team2 = Team.objects.create(organizer=event.organizer, can_create_events=True)
|
||||
team1 = Team.objects.create(organizer=event.organizer, limit_organizer_permissions={"organizer.settings.general:write": True})
|
||||
team2 = Team.objects.create(organizer=event.organizer, limit_organizer_permissions={"organizer.events:create": True})
|
||||
team1.members.add(user)
|
||||
team2.members.add(user)
|
||||
orga2 = Organizer.objects.create(slug='d2', name='d2')
|
||||
team3 = Team.objects.create(organizer=orga2, can_change_teams=True)
|
||||
team3 = Team.objects.create(organizer=orga2, limit_organizer_permissions={"organizer.teams:write": True})
|
||||
team3.members.add(user)
|
||||
|
||||
assert user.has_organizer_permission(event.organizer, 'can_create_events')
|
||||
assert user.has_organizer_permission(event.organizer, 'can_change_organizer_settings')
|
||||
assert not user.has_organizer_permission(event.organizer, 'can_change_teams')
|
||||
assert user.get_organizer_permission_set(event.organizer) == {'can_create_events', 'can_change_organizer_settings'}
|
||||
assert user.get_organizer_permission_set(orga2) == {'can_change_teams'}
|
||||
assert user.has_organizer_permission(event.organizer, 'organizer.events:create')
|
||||
assert user.has_organizer_permission(event.organizer, 'organizer.settings.general:write')
|
||||
assert not user.has_organizer_permission(event.organizer, 'organizer.teams:write')
|
||||
assert user.get_organizer_permission_set(event.organizer) == {
|
||||
'organizer.events:create', 'organizer.settings.general:write',
|
||||
'can_create_events', 'can_change_organizer_settings',
|
||||
}
|
||||
assert user.get_organizer_permission_set(orga2) == {
|
||||
'organizer.teams:write',
|
||||
'can_change_teams',
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_superuser(event, admin, admin_request):
|
||||
assert admin.has_organizer_permission(event.organizer, request=admin_request)
|
||||
assert admin.has_organizer_permission(event.organizer, 'can_create_events', request=admin_request)
|
||||
assert admin.has_organizer_permission(event.organizer, 'organizer.events:create', request=admin_request)
|
||||
assert admin.has_event_permission(event.organizer, event, request=admin_request)
|
||||
assert admin.has_event_permission(event.organizer, event, 'can_change_event_settings', request=admin_request)
|
||||
assert admin.has_event_permission(event.organizer, event, 'event.settings.general:write', request=admin_request)
|
||||
|
||||
assert 'arbitrary' not in admin.get_event_permission_set(event.organizer, event)
|
||||
assert 'arbitrary' not in admin.get_organizer_permission_set(event.organizer)
|
||||
@@ -266,9 +270,9 @@ def test_list_of_events(event, user, admin, admin_request):
|
||||
|
||||
assert not user.get_events_with_any_permission()
|
||||
|
||||
team1 = Team.objects.create(organizer=event.organizer, can_change_orders=True, all_events=True)
|
||||
team2 = Team.objects.create(organizer=event.organizer, can_change_vouchers=True)
|
||||
team3 = Team.objects.create(organizer=orga2, can_change_event_settings=True)
|
||||
team1 = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.orders:write": True}, all_events=True)
|
||||
team2 = Team.objects.create(organizer=event.organizer, limit_event_permissions={"event.vouchers:write": True})
|
||||
team3 = Team.objects.create(organizer=orga2, limit_event_permissions={"event.settings.general:write": True})
|
||||
team1.members.add(user)
|
||||
team2.members.add(user)
|
||||
team3.members.add(user)
|
||||
@@ -282,7 +286,7 @@ def test_list_of_events(event, user, admin, admin_request):
|
||||
assert event3 in events
|
||||
assert event4 not in events
|
||||
|
||||
events = list(user.get_events_with_permission('can_change_event_settings', request=admin_request))
|
||||
events = list(user.get_events_with_permission('event.settings.general:write', request=admin_request))
|
||||
assert event not in events
|
||||
assert event2 not in events
|
||||
assert event3 in events
|
||||
@@ -293,8 +297,73 @@ def test_list_of_events(event, user, admin, admin_request):
|
||||
assert set(event3.get_users_with_any_permission()) == {user}
|
||||
assert set(event4.get_users_with_any_permission()) == set()
|
||||
|
||||
assert set(event.get_users_with_permission('can_change_event_settings')) == set()
|
||||
assert set(event2.get_users_with_permission('can_change_event_settings')) == set()
|
||||
assert set(event3.get_users_with_permission('can_change_event_settings')) == {user}
|
||||
assert set(event4.get_users_with_permission('can_change_event_settings')) == set()
|
||||
assert set(event.get_users_with_permission('can_change_orders')) == {user}
|
||||
assert set(event.get_users_with_permission('event.settings.general:write')) == set()
|
||||
assert set(event2.get_users_with_permission('event.settings.general:write')) == set()
|
||||
assert set(event3.get_users_with_permission('event.settings.general:write')) == {user}
|
||||
assert set(event4.get_users_with_permission('event.settings.general:write')) == set()
|
||||
assert set(event.get_users_with_permission('event.orders:write')) == {user}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.filterwarnings("ignore")
|
||||
def test_check_with_legacy_permission_names(event, user):
|
||||
team1 = Team.objects.create(
|
||||
organizer=event.organizer,
|
||||
limit_event_permissions={"event.settings.general:write": True},
|
||||
limit_organizer_permissions={
|
||||
"organizer.giftcards:read": True,
|
||||
"organizer.giftcards:write": True,
|
||||
"organizer.reusablemedia:write": True,
|
||||
},
|
||||
all_events=True
|
||||
)
|
||||
team1.members.add(user)
|
||||
|
||||
# Team methods
|
||||
assert team1.has_event_permission('can_change_event_settings')
|
||||
assert team1.has_event_permission('can_change_settings')
|
||||
assert not team1.has_event_permission('can_view_orders')
|
||||
assert team1.has_organizer_permission('can_manage_gift_cards')
|
||||
assert not team1.has_organizer_permission('can_manage_reusable_media')
|
||||
assert team1.organizer_permission_set() == {
|
||||
"organizer.giftcards:read",
|
||||
"organizer.giftcards:write",
|
||||
"organizer.reusablemedia:write",
|
||||
"can_manage_gift_cards",
|
||||
}
|
||||
assert team1.organizer_permission_set(include_legacy=False) == {
|
||||
"organizer.giftcards:read",
|
||||
"organizer.giftcards:write",
|
||||
"organizer.reusablemedia:write",
|
||||
}
|
||||
assert team1.event_permission_set() == {
|
||||
"event.settings.general:write", "can_change_event_settings", "can_change_settings",
|
||||
}
|
||||
assert team1.event_permission_set(include_legacy=False) == {
|
||||
"event.settings.general:write",
|
||||
}
|
||||
|
||||
# User methods
|
||||
user._teamcache = {}
|
||||
assert user.get_event_permission_set(event.organizer, event) == {
|
||||
"event.settings.general:write", "can_change_event_settings", "can_change_settings",
|
||||
}
|
||||
assert user.get_organizer_permission_set(event.organizer) == {
|
||||
"organizer.giftcards:read",
|
||||
"organizer.giftcards:write",
|
||||
"organizer.reusablemedia:write",
|
||||
"can_manage_gift_cards",
|
||||
}
|
||||
assert user.has_event_permission(event.organizer, event, 'can_change_event_settings')
|
||||
assert user.has_event_permission(event.organizer, event, 'can_change_settings')
|
||||
assert not user.has_event_permission(event.organizer, event, 'can_view_orders')
|
||||
assert user.has_organizer_permission(event.organizer, 'can_manage_gift_cards')
|
||||
assert not user.has_organizer_permission(event.organizer, 'can_manage_reusable_media')
|
||||
assert user.get_events_with_permission("can_change_event_settings").get() == event
|
||||
assert not user.get_events_with_permission("can_view_orders").exists()
|
||||
assert user.get_organizers_with_permission("can_manage_gift_cards").get() == event.organizer
|
||||
assert not user.get_organizers_with_permission("can_manage_reusable_media").exists()
|
||||
|
||||
# Event methods
|
||||
assert event.get_users_with_permission("can_change_event_settings").get() == user
|
||||
assert not event.get_users_with_permission("can_view_orders").exists()
|
||||
|
||||
Reference in New Issue
Block a user