forked from CGM_Public/pretix_original
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:
@@ -62,6 +62,7 @@ from pretix.base.forms import (
|
||||
)
|
||||
from pretix.base.models import Event, Organizer, TaxRule, Team
|
||||
from pretix.base.models.event import EventFooterLink, EventMetaValue, SubEvent
|
||||
from pretix.base.models.organizer import TeamQuerySet
|
||||
from pretix.base.models.tax import TAX_CODE_LISTS
|
||||
from pretix.base.reldate import RelativeDateField, RelativeDateTimeField
|
||||
from pretix.base.services.placeholders import FormPlaceholderMixin
|
||||
@@ -100,11 +101,12 @@ class EventWizardFoundationForm(forms.Form):
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.user = kwargs.pop('user')
|
||||
self.session = kwargs.pop('session')
|
||||
self.clone_from = kwargs.pop('clone_from')
|
||||
super().__init__(*args, **kwargs)
|
||||
qs = Organizer.objects.all()
|
||||
if not self.user.has_active_staff_session(self.session.session_key):
|
||||
qs = qs.filter(
|
||||
id__in=self.user.teams.filter(can_create_events=True).values_list('organizer', flat=True)
|
||||
id__in=self.user.teams.filter(TeamQuerySet.organizer_permission_q("organizer.events:create")).values_list('organizer', flat=True)
|
||||
)
|
||||
self.fields['organizer'] = forms.ModelChoiceField(
|
||||
label=_("Organizer"),
|
||||
@@ -125,6 +127,16 @@ class EventWizardFoundationForm(forms.Form):
|
||||
self.fields['organizer'].initial = organizer
|
||||
self.fields['locales'].initial = organizer.settings.locales
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('organizer') and self.clone_from and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if not self.clone_from.allow_copy_data(d['organizer'], self.user):
|
||||
raise ValidationError({
|
||||
"organizer": _("You do not have a sufficient level of access on the event you selected "
|
||||
"to copy it to the desired organizer.")
|
||||
})
|
||||
return d
|
||||
|
||||
|
||||
class EventWizardBasicsForm(I18nModelForm):
|
||||
error_messages = {
|
||||
@@ -198,6 +210,7 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
self.has_subevents = kwargs.pop('has_subevents')
|
||||
self.user = kwargs.pop('user')
|
||||
self.session = kwargs.pop('session')
|
||||
self.clone_from = kwargs.pop('clone_from')
|
||||
super().__init__(*args, **kwargs)
|
||||
if 'timezone' not in self.initial:
|
||||
self.initial['timezone'] = get_current_timezone_name()
|
||||
@@ -238,6 +251,16 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
'check "{field}" above.').format(field=self.fields["no_taxes"].label)
|
||||
})
|
||||
|
||||
if self.clone_from and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if data.get("team"):
|
||||
source_event_perms = self.user.get_event_permission_set(self.organizer, self.clone_from)
|
||||
team_perms = data["team"].event_permission_set(include_legacy=False)
|
||||
if any(t not in source_event_perms for t in team_perms):
|
||||
raise ValidationError({
|
||||
"team": _("You cannot choose a team that would give you more access than you have on "
|
||||
"the event you are copying.")
|
||||
})
|
||||
|
||||
# change timezone
|
||||
zone = ZoneInfo(data.get('timezone'))
|
||||
data['date_from'] = self.reset_timezone(zone, data.get('date_from'))
|
||||
@@ -261,9 +284,12 @@ class EventWizardBasicsForm(I18nModelForm):
|
||||
|
||||
@staticmethod
|
||||
def has_control_rights(user, organizer, session):
|
||||
# It's mostly pointless to let a user create an event where they can't event change the name or create products,
|
||||
# so we detect if the user has sufficient access for that on a new event.
|
||||
return user.teams.filter(
|
||||
organizer=organizer, all_events=True, can_change_event_settings=True, can_change_items=True,
|
||||
can_change_orders=True, can_change_vouchers=True
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
organizer=organizer,
|
||||
all_events=True,
|
||||
).exists() or user.has_active_staff_session(session.session_key)
|
||||
|
||||
|
||||
@@ -293,18 +319,24 @@ class EventWizardCopyForm(forms.Form):
|
||||
if user.has_active_staff_session(session.session_key):
|
||||
return Event.objects.all()
|
||||
return Event.objects.filter(
|
||||
# It is generally pointless to let users copy events when they would not even be able to change the
|
||||
# date of the event they have just created. Therefore, even if it looks wrong, we're checking a write
|
||||
# permission for read access.
|
||||
Q(organizer_id__in=user.teams.filter(
|
||||
all_events=True, can_change_event_settings=True, can_change_items=True
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
all_events=True,
|
||||
).values_list('organizer', flat=True)) | Q(id__in=user.teams.filter(
|
||||
can_change_event_settings=True, can_change_items=True
|
||||
TeamQuerySet.event_permission_q("event.settings.general:write"),
|
||||
).values_list('limit_events__id', flat=True))
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs.pop('organizer')
|
||||
self.organizer = kwargs.pop('organizer')
|
||||
kwargs.pop('locales')
|
||||
self.session = kwargs.pop('session')
|
||||
self.team = kwargs.pop('team')
|
||||
kwargs.pop('has_subevents')
|
||||
kwargs.pop('clone_from')
|
||||
self.user = kwargs.pop('user')
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
@@ -323,6 +355,24 @@ class EventWizardCopyForm(forms.Form):
|
||||
)
|
||||
self.fields['copy_from_event'].widget.choices = self.fields['copy_from_event'].choices
|
||||
|
||||
def clean(self):
|
||||
d = super().clean()
|
||||
if d.get('copy_from_event') and not self.user.has_active_staff_session(self.session.session_key):
|
||||
if not d['copy_from_event'].allow_copy_data(self.organizer, self.user):
|
||||
raise ValidationError({
|
||||
"copy_from_event": _("You do not have a sufficient level of access on the event you selected "
|
||||
"to copy it to the desired organizer.")
|
||||
})
|
||||
if self.team:
|
||||
source_event_perms = self.user.get_event_permission_set(self.organizer, d['copy_from_event'])
|
||||
team_perms = self.team.event_permission_set(include_legacy=False)
|
||||
if any(t not in source_event_perms for t in team_perms):
|
||||
raise ValidationError({
|
||||
"copy_from_event": _("You cannot choose an event on which you have less access than the "
|
||||
"team you selected in the previous step.")
|
||||
})
|
||||
return d
|
||||
|
||||
|
||||
class EventMetaValueForm(forms.ModelForm):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user