mirror of
https://github.com/pretix/pretix.git
synced 2026-05-07 15:34:02 +00:00
Make tests pass
This commit is contained in:
@@ -341,7 +341,7 @@ class CloneEventViewSet(viewsets.ModelViewSet):
|
||||
lookup_field = 'slug'
|
||||
lookup_url_kwarg = 'event'
|
||||
http_method_names = ['post']
|
||||
write_permission = 'organizer.events:create'
|
||||
write_permission = 'event.settings.general:write'
|
||||
|
||||
def get_serializer_context(self):
|
||||
ctx = super().get_serializer_context()
|
||||
@@ -350,6 +350,12 @@ class CloneEventViewSet(viewsets.ModelViewSet):
|
||||
return ctx
|
||||
|
||||
def perform_create(self, serializer):
|
||||
# Weird edge case: Requires settings permission on the event (to read) but also on the organizer (two write)
|
||||
perm_holder = (self.request.auth if isinstance(self.request.auth, (Device, TeamAPIToken))
|
||||
else self.request.user)
|
||||
if not perm_holder.has_organizer_permission(self.request.organizer, "organizer.events:create", request=self.request):
|
||||
raise PermissionDenied("No permission to create events")
|
||||
|
||||
serializer.save(organizer=self.request.organizer)
|
||||
|
||||
serializer.instance.log_action(
|
||||
|
||||
@@ -1942,10 +1942,15 @@ class InvoiceViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
ordering = ('nr',)
|
||||
ordering_fields = ('nr', 'date')
|
||||
filterset_class = InvoiceFilter
|
||||
permission = 'event.orders:read'
|
||||
lookup_url_kwarg = 'number'
|
||||
lookup_field = 'nr'
|
||||
write_permission = 'event.orders:write'
|
||||
|
||||
def _get_permission_name(self, request):
|
||||
if 'event' in request.resolver_match.kwargs:
|
||||
if request.method not in SAFE_METHODS:
|
||||
return "event.orders:write"
|
||||
return "event.orders:read"
|
||||
return None # org-level is handled by event__in check
|
||||
|
||||
def get_queryset(self):
|
||||
perm = "event.orders:read" if self.request.method in SAFE_METHODS else "event.orders:write"
|
||||
|
||||
@@ -472,7 +472,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
:return: set
|
||||
"""
|
||||
teams = self._get_teams_for_event(organizer, event)
|
||||
sets = [t.permission_set() for t in teams]
|
||||
sets = [t.event_permission_set() for t in teams]
|
||||
if sets:
|
||||
return set.union(*sets)
|
||||
else:
|
||||
@@ -486,7 +486,7 @@ class User(AbstractBaseUser, PermissionsMixin, LoggingMixin):
|
||||
:return: set
|
||||
"""
|
||||
teams = self._get_teams_for_organizer(organizer)
|
||||
sets = [t.permission_set() for t in teams]
|
||||
sets = [t.organizer_permission_set() for t in teams]
|
||||
if sets:
|
||||
return set.union(*sets)
|
||||
else:
|
||||
|
||||
@@ -189,11 +189,15 @@ class Device(LoggedModel):
|
||||
kwargs['update_fields'] = {'device_id'}.union(kwargs['update_fields'])
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def permission_set(self) -> set:
|
||||
def _event_permission_set(self) -> set:
|
||||
return {
|
||||
'event.orders:read',
|
||||
'event.orders:write',
|
||||
'event.vouchers:read',
|
||||
}
|
||||
|
||||
def _organizer_permission_set(self) -> set:
|
||||
return {
|
||||
'organizer.giftcards:read',
|
||||
'organizer.giftcards:write',
|
||||
'organizer.reusablemedia:read',
|
||||
@@ -211,7 +215,7 @@ class Device(LoggedModel):
|
||||
has_event_access = (self.all_events and organizer == self.organizer) or (
|
||||
event in self.limit_events.all()
|
||||
)
|
||||
return self.permission_set() if has_event_access else set()
|
||||
return self._event_permission_set() if has_event_access else set()
|
||||
|
||||
def get_organizer_permission_set(self, organizer) -> set:
|
||||
"""
|
||||
@@ -220,7 +224,7 @@ class Device(LoggedModel):
|
||||
:param organizer: The organizer of the event
|
||||
:return: set of permissions
|
||||
"""
|
||||
return self.permission_set() if self.organizer == organizer else set()
|
||||
return self._organizer_permission_set() if self.organizer == organizer else set()
|
||||
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None) -> bool:
|
||||
"""
|
||||
@@ -237,8 +241,8 @@ class Device(LoggedModel):
|
||||
event in self.limit_events.all()
|
||||
)
|
||||
if isinstance(perm_name, (tuple, list)):
|
||||
return has_event_access and any(p in self.permission_set() for p in perm_name)
|
||||
return has_event_access and (not perm_name or perm_name in self.permission_set())
|
||||
return has_event_access and any(p in self._event_permission_set() for p in perm_name)
|
||||
return has_event_access and (not perm_name or perm_name in self._event_permission_set())
|
||||
|
||||
def has_organizer_permission(self, organizer, perm_name=None, request=None):
|
||||
"""
|
||||
@@ -251,8 +255,8 @@ class Device(LoggedModel):
|
||||
:return: bool
|
||||
"""
|
||||
if isinstance(perm_name, (tuple, list)):
|
||||
return organizer == self.organizer and any(p in self.permission_set() for p in perm_name)
|
||||
return organizer == self.organizer and (not perm_name or perm_name in self.permission_set())
|
||||
return organizer == self.organizer and any(p in self._organizer_permission_set() for p in perm_name)
|
||||
return organizer == self.organizer and (not perm_name or perm_name in self._organizer_permission_set())
|
||||
|
||||
def get_events_with_any_permission(self):
|
||||
"""
|
||||
@@ -273,8 +277,8 @@ class Device(LoggedModel):
|
||||
:return: Iterable of Events
|
||||
"""
|
||||
if (
|
||||
isinstance(permission, (list, tuple)) and any(p in self.permission_set() for p in permission)
|
||||
) or (isinstance(permission, str) and permission in self.permission_set()):
|
||||
isinstance(permission, (list, tuple)) and any(p in self._event_permission_set() for p in permission)
|
||||
) or (isinstance(permission, str) and permission in self._event_permission_set()):
|
||||
return self.get_events_with_any_permission()
|
||||
else:
|
||||
return self.organizer.events.none()
|
||||
|
||||
@@ -1386,14 +1386,13 @@ class Event(EventMixin, LoggedModel):
|
||||
from .auth import User
|
||||
|
||||
if permission:
|
||||
kwargs = {permission: True}
|
||||
qs = Team.objects.with_event_permission(permission)
|
||||
else:
|
||||
kwargs = {}
|
||||
qs = Team.objects.all()
|
||||
|
||||
team_with_perm = Team.objects.filter(
|
||||
team_with_perm = qs.filter(
|
||||
members__pk=OuterRef('pk'),
|
||||
organizer=self.organizer,
|
||||
**kwargs
|
||||
).filter(
|
||||
Q(all_events=True) | Q(limit_events__pk=self.pk)
|
||||
)
|
||||
|
||||
@@ -397,26 +397,32 @@ class Team(LoggedModel):
|
||||
'object': str(self.organizer),
|
||||
}
|
||||
|
||||
def permission_set(self, include_legacy=True) -> set:
|
||||
from ..permissions import (
|
||||
get_all_event_permissions, get_all_organizer_permissions,
|
||||
)
|
||||
def event_permission_set(self, include_legacy=True) -> set:
|
||||
from ..permissions import get_all_event_permissions
|
||||
|
||||
result = set()
|
||||
for permission in get_all_event_permissions().keys():
|
||||
if self.all_event_permissions or self.limit_event_permissions.get(permission):
|
||||
result.add(permission)
|
||||
|
||||
for permission in get_all_organizer_permissions().keys():
|
||||
if self.all_organizer_permissions or self.limit_organizer_permissions.get(permission):
|
||||
result.add(permission)
|
||||
|
||||
if include_legacy:
|
||||
# Add legacy permissions as well for plugin compatibility
|
||||
for k, v in OLD_TO_NEW_EVENT_COMPAT.items():
|
||||
if self.all_event_permissions or all(self.limit_event_permissions.get(kk) for kk in v):
|
||||
result.add(k)
|
||||
|
||||
return result
|
||||
|
||||
def organizer_permission_set(self, include_legacy=True) -> set:
|
||||
from ..permissions import get_all_organizer_permissions
|
||||
|
||||
result = set()
|
||||
for permission in get_all_organizer_permissions().keys():
|
||||
if self.all_organizer_permissions or self.limit_organizer_permissions.get(permission):
|
||||
result.add(permission)
|
||||
|
||||
if include_legacy:
|
||||
# Add legacy permissions as well for plugin compatibility
|
||||
for k, v in OLD_TO_NEW_ORGANIZER_COMPAT.items():
|
||||
if self.all_organizer_permissions or all(self.limit_organizer_permissions.get(kk) for kk in v):
|
||||
result.add(k)
|
||||
@@ -516,7 +522,7 @@ class TeamAPIToken(models.Model):
|
||||
has_event_access = (self.team.all_events and organizer == self.team.organizer) or (
|
||||
event in self.team.limit_events.all()
|
||||
)
|
||||
return self.team.permission_set() if has_event_access else set()
|
||||
return self.team.event_permission_set() if has_event_access else set()
|
||||
|
||||
def get_organizer_permission_set(self, organizer) -> set:
|
||||
"""
|
||||
@@ -525,7 +531,7 @@ class TeamAPIToken(models.Model):
|
||||
:param organizer: The organizer of the event
|
||||
:return: set of permissions
|
||||
"""
|
||||
return self.team.permission_set() if self.team.organizer == organizer else set()
|
||||
return self.team.organizer_permission_set() if self.team.organizer == organizer else set()
|
||||
|
||||
def has_event_permission(self, organizer, event, perm_name=None, request=None) -> bool:
|
||||
"""
|
||||
|
||||
@@ -376,7 +376,7 @@ class TeamForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
data = super().clean()
|
||||
if self.instance.pk and not data['all_organizer_permissions'] and not data.get('limit_organizer_permissions', {}).get('organizer.teams:write'):
|
||||
if self.instance.pk and not data['all_organizer_permissions'] and 'organizer.teams:write' not in data.get('limit_organizer_permissions', []):
|
||||
if not self.instance.organizer.teams.exclude(pk=self.instance.pk).filter(
|
||||
TeamQuerySet.organizer_permission_q("organizer.teams:write"),
|
||||
members__isnull=False
|
||||
|
||||
@@ -245,7 +245,7 @@ class OrganizerDetail(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin
|
||||
class OrganizerTeamView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
model = Organizer
|
||||
template_name = 'pretixcontrol/organizers/teams.html'
|
||||
permission = 'organizer.teams:read'
|
||||
permission = 'organizer.teams:write'
|
||||
context_object_name = 'organizer'
|
||||
|
||||
|
||||
@@ -1068,7 +1068,7 @@ class TeamMemberView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin,
|
||||
TeamQuerySet.organizer_permission_q("organizer.teams:write"),
|
||||
members__isnull=False
|
||||
).exists() or self.request.user.has_active_staff_session(self.request.session.session_key)
|
||||
if not other_admin_teams and self.object.has_permission() and self.object.members.count() == 1:
|
||||
if not other_admin_teams and self.object.has_organizer_permission("organizer.teams:write") and self.object.members.count() == 1:
|
||||
messages.error(self.request, _('You cannot remove the last member from this team as no one would '
|
||||
'be left with the permission to change teams.'))
|
||||
return redirect(self.get_success_url())
|
||||
@@ -1754,7 +1754,7 @@ class GiftCardAcceptanceListView(OrganizerDetailViewMixin, OrganizerPermissionRe
|
||||
class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, ListView):
|
||||
model = GiftCard
|
||||
template_name = 'pretixcontrol/organizers/giftcards.html'
|
||||
permission = 'organizer.giftcards:write'
|
||||
permission = 'organizer.giftcards:read'
|
||||
context_object_name = 'giftcards'
|
||||
paginate_by = 50
|
||||
|
||||
@@ -1788,7 +1788,7 @@ class GiftCardListView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixi
|
||||
|
||||
class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMixin, DetailView):
|
||||
template_name = 'pretixcontrol/organizers/giftcard.html'
|
||||
permission = 'organizer.giftcards:write'
|
||||
permission = 'organizer.giftcards:read'
|
||||
context_object_name = 'card'
|
||||
|
||||
def get_object(self, queryset=None) -> Organizer:
|
||||
@@ -1799,6 +1799,8 @@ class GiftCardDetailView(OrganizerDetailViewMixin, OrganizerPermissionRequiredMi
|
||||
|
||||
@transaction.atomic()
|
||||
def post(self, request, *args, **kwargs):
|
||||
if not request.user.has_organizer_permission(request.organizer, "organizer.giftcards:write", request=request):
|
||||
raise PermissionDenied()
|
||||
self.object = GiftCard.objects.select_for_update(of=OF_SELF).get(pk=self.get_object().pk)
|
||||
if 'revert' in request.POST:
|
||||
t = get_object_or_404(self.object.transactions.all(), pk=request.POST.get('revert'), order__isnull=False)
|
||||
|
||||
@@ -45,7 +45,7 @@ OLD_TO_NEW_ORGANIZER_MIGRATION = {
|
||||
"can_create_events": ["organizer.events:create"],
|
||||
"can_change_organizer_settings": ["organizer.settings.general:write", "organizer.devices:read",
|
||||
"organizer.devices:write"],
|
||||
"can_change_teams": ["organizer.teams:write", "organizer.teams:read"],
|
||||
"can_change_teams": ["organizer.teams:write"],
|
||||
"can_manage_gift_cards": ["organizer.giftcards:read", "organizer.giftcards:write"],
|
||||
"can_manage_customers": ["organizer.customers:read", "organizer.customers:write"],
|
||||
"can_manage_reusable_media": ["organizer.reusablemedia:read", "organizer.reusablemedia:write"],
|
||||
@@ -66,7 +66,7 @@ OLD_TO_NEW_EVENT_COMPAT = {
|
||||
OLD_TO_NEW_ORGANIZER_COMPAT = {
|
||||
"can_create_events": ["organizer.events:create"],
|
||||
"can_change_organizer_settings": ["organizer.settings.general:write"],
|
||||
"can_change_teams": ["organizer.teams:write", "organizer.teams:read"],
|
||||
"can_change_teams": ["organizer.teams:write"],
|
||||
"can_manage_gift_cards": ["organizer.giftcards:read", "organizer.giftcards:write"],
|
||||
"can_manage_customers": ["organizer.customers:read", "organizer.customers:write"],
|
||||
"can_manage_reusable_media": ["organizer.reusablemedia:read", "organizer.reusablemedia:write"],
|
||||
|
||||
@@ -97,7 +97,6 @@ class BankImportJobViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
queryset = BankImportJob.objects.none()
|
||||
filter_backends = (DjangoFilterBackend,)
|
||||
filterset_class = JobFilter
|
||||
permission = 'event.orders:read'
|
||||
|
||||
def get_queryset(self):
|
||||
return BankImportJob.objects.filter(organizer=self.request.organizer)
|
||||
@@ -105,9 +104,30 @@ class BankImportJobViewSet(CreateModelMixin, viewsets.ReadOnlyModelViewSet):
|
||||
def perform_create(self, serializer):
|
||||
return serializer.save()
|
||||
|
||||
def retrieve(self, request, *args, **kwargs):
|
||||
perm_holder = (request.auth if isinstance(request.auth, (Device, TeamAPIToken)) else request.user)
|
||||
has_any_event_perm = perm_holder.get_events_with_permission(
|
||||
"event.orders:read", request=request
|
||||
).filter(organizer=request.organizer).exists()
|
||||
if not has_any_event_perm:
|
||||
raise PermissionDenied('Invalid set of permissions')
|
||||
return super().retrieve(request, *args, **kwargs)
|
||||
|
||||
def list(self, request, *args, **kwargs):
|
||||
perm_holder = (request.auth if isinstance(request.auth, (Device, TeamAPIToken)) else request.user)
|
||||
has_any_event_perm = perm_holder.get_events_with_permission(
|
||||
"event.orders:read", request=request
|
||||
).filter(organizer=request.organizer).exists()
|
||||
if not has_any_event_perm:
|
||||
raise PermissionDenied('Invalid set of permissions')
|
||||
return super().list(request, *args, **kwargs)
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
perm_holder = (request.auth if isinstance(request.auth, (Device, TeamAPIToken)) else request.user)
|
||||
if not perm_holder.has_organizer_permission(request.organizer, 'event.orders:write'):
|
||||
has_any_event_perm = perm_holder.get_events_with_permission(
|
||||
"event.orders:write", request=request
|
||||
).filter(organizer=request.organizer).exists()
|
||||
if not has_any_event_perm:
|
||||
raise PermissionDenied('Invalid set of permissions')
|
||||
|
||||
if BankImportJob.objects.filter(Q(organizer=request.organizer)).filter(
|
||||
|
||||
@@ -76,7 +76,10 @@ def control_nav_import(sender, request=None, **kwargs):
|
||||
@receiver(nav_organizer, dispatch_uid="payment_banktransfer_organav")
|
||||
def control_nav_orga_import(sender, request=None, **kwargs):
|
||||
url = resolve(request.path_info)
|
||||
if not request.user.has_organizer_permission(request.organizer, 'event.orders:write', request=request):
|
||||
has_any_event_perm = request.user.get_events_with_permission(
|
||||
"event.orders:write", request=request
|
||||
).filter(organizer=request.organizer).exists()
|
||||
if not has_any_event_perm:
|
||||
return []
|
||||
return [
|
||||
{
|
||||
|
||||
@@ -44,6 +44,7 @@ from typing import Set
|
||||
|
||||
from django import forms
|
||||
from django.contrib import messages
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.db import transaction
|
||||
from django.db.models import Count, Q, QuerySet
|
||||
from django.http import FileResponse, JsonResponse
|
||||
@@ -61,9 +62,7 @@ from pretix.base.models import Event, Order, OrderPayment, OrderRefund, Quota
|
||||
from pretix.base.models.organizer import TeamQuerySet
|
||||
from pretix.base.settings import SettingsSandbox
|
||||
from pretix.base.templatetags.money import money_filter
|
||||
from pretix.control.permissions import (
|
||||
EventPermissionRequiredMixin, OrganizerPermissionRequiredMixin,
|
||||
)
|
||||
from pretix.control.permissions import EventPermissionRequiredMixin
|
||||
from pretix.control.views.organizer import OrganizerDetailViewMixin
|
||||
from pretix.helpers.json import CustomJSONEncoder
|
||||
from pretix.plugins.banktransfer import camtimport, csvimport, mt940import
|
||||
@@ -626,6 +625,11 @@ class ImportView(ListView):
|
||||
|
||||
class OrganizerBanktransferView:
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
has_any_event_perm = request.user.get_events_with_permission(
|
||||
"event.orders:write", request=request
|
||||
).filter(organizer=request.organizer).exists()
|
||||
if not has_any_event_perm:
|
||||
raise PermissionDenied()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
||||
@@ -633,27 +637,26 @@ class EventImportView(EventPermissionRequiredMixin, ImportView):
|
||||
permission = 'event.orders:write'
|
||||
|
||||
|
||||
class OrganizerImportView(OrganizerBanktransferView, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin,
|
||||
class OrganizerImportView(OrganizerBanktransferView, OrganizerDetailViewMixin,
|
||||
ImportView):
|
||||
permission = 'event.orders:write'
|
||||
pass
|
||||
|
||||
|
||||
class EventJobDetailView(EventPermissionRequiredMixin, JobDetailView):
|
||||
permission = 'event.orders:write'
|
||||
|
||||
|
||||
class OrganizerJobDetailView(OrganizerBanktransferView, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin,
|
||||
class OrganizerJobDetailView(OrganizerBanktransferView, OrganizerDetailViewMixin,
|
||||
JobDetailView):
|
||||
permission = 'event.orders:write'
|
||||
pass
|
||||
|
||||
|
||||
class EventActionView(EventPermissionRequiredMixin, ActionView):
|
||||
permission = 'event.orders:write'
|
||||
|
||||
|
||||
class OrganizerActionView(OrganizerBanktransferView, OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin,
|
||||
class OrganizerActionView(OrganizerBanktransferView, OrganizerDetailViewMixin,
|
||||
ActionView):
|
||||
permission = 'event.orders:write'
|
||||
|
||||
def order_qs(self):
|
||||
all = self.request.user.teams.filter(
|
||||
@@ -784,8 +787,7 @@ class EventRefundExportListView(EventPermissionRequiredMixin, RefundExportListVi
|
||||
)
|
||||
|
||||
|
||||
class OrganizerRefundExportListView(OrganizerPermissionRequiredMixin, RefundExportListView):
|
||||
permission = 'event.orders:write'
|
||||
class OrganizerRefundExportListView(OrganizerBanktransferView, RefundExportListView):
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('plugins:banktransfer:refunds.list', kwargs={
|
||||
@@ -828,8 +830,7 @@ class EventDownloadRefundExportView(EventPermissionRequiredMixin, DownloadRefund
|
||||
)
|
||||
|
||||
|
||||
class OrganizerDownloadRefundExportView(OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin, DownloadRefundExportView):
|
||||
permission = 'event.orders:write'
|
||||
class OrganizerDownloadRefundExportView(OrganizerBanktransferView, OrganizerDetailViewMixin, DownloadRefundExportView):
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
return get_object_or_404(
|
||||
@@ -857,9 +858,9 @@ class SepaXMLExportView(SingleObjectMixin, FormView):
|
||||
template_name = 'pretixplugins/banktransfer/sepa_export.html'
|
||||
context_object_name = "export"
|
||||
|
||||
def setup(self, request, *args, **kwargs):
|
||||
super().setup(request, *args, **kwargs)
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
self.object: RefundExport = self.get_object()
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
self.object.downloaded = True
|
||||
@@ -891,8 +892,7 @@ class EventSepaXMLExportView(EventPermissionRequiredMixin, SepaXMLExportView):
|
||||
return form
|
||||
|
||||
|
||||
class OrganizerSepaXMLExportView(OrganizerPermissionRequiredMixin, OrganizerDetailViewMixin, SepaXMLExportView):
|
||||
permission = 'event.orders:write'
|
||||
class OrganizerSepaXMLExportView(OrganizerBanktransferView, OrganizerDetailViewMixin, SepaXMLExportView):
|
||||
|
||||
def get_object(self, *args, **kwargs):
|
||||
return get_object_or_404(
|
||||
|
||||
Reference in New Issue
Block a user