Handle permissions on event copying

This commit is contained in:
Raphael Michel
2026-03-09 14:57:31 +01:00
parent a0fd10d238
commit 0269832570
7 changed files with 442 additions and 32 deletions
+1 -1
View File
@@ -445,7 +445,7 @@ class CloneEventSerializer(EventSerializer):
date_admission = validated_data.pop('date_admission', None)
new_event = super().create({**validated_data, 'plugins': None})
event = Event.objects.filter(slug=self.context['event'], organizer=self.context['organizer'].pk).first()
event = self.context['event']
new_event.copy_data_from(event, skip_meta_data='meta_data' in validated_data)
if plugins is not None:
+9 -1
View File
@@ -281,6 +281,11 @@ class EventViewSet(viewsets.ModelViewSet):
new_event = serializer.save(organizer=self.request.organizer)
if copy_from:
perm_holder = (self.request.auth if isinstance(self.request.auth, (Device, TeamAPIToken))
else self.request.user)
if not copy_from.allow_copy_data(self.request.organizer, perm_holder):
raise PermissionDenied("Not sufficient permission on source event to copy")
new_event.copy_data_from(copy_from, skip_meta_data='meta_data' in serializer.validated_data)
if plugins is not None:
@@ -345,7 +350,7 @@ class CloneEventViewSet(viewsets.ModelViewSet):
def get_serializer_context(self):
ctx = super().get_serializer_context()
ctx['event'] = self.kwargs['event']
ctx['event'] = Event.objects.get(slug=self.kwargs['event'], organizer=self.request.organizer)
ctx['organizer'] = self.request.organizer
return ctx
@@ -356,6 +361,9 @@ class CloneEventViewSet(viewsets.ModelViewSet):
if not perm_holder.has_organizer_permission(self.request.organizer, "organizer.events:create", request=self.request):
raise PermissionDenied("No permission to create events")
if not serializer.context['event'].allow_copy_data(self.request.organizer, perm_holder):
raise PermissionDenied("Not sufficient permission on source event to copy")
serializer.save(organizer=self.request.organizer)
serializer.instance.log_action(
+27
View File
@@ -843,6 +843,33 @@ class Event(EventMixin, LoggedModel):
time(hour=23, minute=59, second=59)
), tz)
def allow_copy_data(self, new_organizer, auth) -> bool:
"""
Returns whether it is allowed to copy the event to the target organizer. Auth can be TeamAPIToken or User.
"""
from ..permissions import get_all_event_permissions
from .auth import User
if self.organizer == new_organizer:
# Copying in the same organizer is always okay with any read access, we just need to ensure it does not
# grant more permissions than I had before, but that is handled by the view logic
return auth.has_event_permission(self.organizer, self, None)
if isinstance(auth, User):
# Cross-organizer copying requires almost full permission of source to prevent settings extraction
required_permissions = get_all_event_permissions() - {
# We do not require these, as this data is not copied
"event:orders.read", "event:orders.write", "event:vouchers.read", "event:vouchers.write",
"event:subevents.write",
}
given_permission = auth.get_event_permission_set(self.organizer, self)
return all(p in given_permission for p in required_permissions if ":" not in p)
else:
# Tokens or devices can never copy between organizers, as they are organizer-bound. Kept for future
# compatibility and easier calling
return False
def copy_data_from(self, other, skip_meta_data=False):
from ..signals import event_copy_data
from . import (
+50 -8
View File
@@ -101,6 +101,7 @@ 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):
@@ -126,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 = {
@@ -199,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()
@@ -239,6 +251,18 @@ 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)
print(source_event_perms)
print(team_perms)
if any(t not in source_event_perms for t in team_perms if ":" in t):
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'))
@@ -262,10 +286,9 @@ 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(
TeamQuerySet.event_permission_q("event.items:write"),
TeamQuerySet.event_permission_q("event.orders:write"),
TeamQuerySet.event_permission_q("event.vouchers:write"),
TeamQuerySet.event_permission_q("event.settings.general:write"),
organizer=organizer,
all_events=True,
@@ -298,23 +321,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(
# TODO: review these!
# Restrict cross-organizer copying further than same-organizer copying?
TeamQuerySet.event_permission_q("event.settings.general:write"),
TeamQuerySet.event_permission_q("event.items:write"),
all_events=True,
).values_list('organizer', flat=True)) | Q(id__in=user.teams.filter(
TeamQuerySet.event_permission_q("event.settings.general:write"),
TeamQuerySet.event_permission_q("event.items: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)
@@ -333,6 +357,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 if ":" in t):
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):
+41 -19
View File
@@ -211,12 +211,7 @@ class EventWizard(SafeSessionWizardView):
except Event.DoesNotExist:
allow = False
else:
allow = (
request.user.has_event_permission(clone_from.organizer, clone_from,
'event.settings.general:write', request)
and request.user.has_event_permission(clone_from.organizer, clone_from,
'event.items:write', request)
)
allow = request.user.has_event_permission(clone_from.organizer, clone_from, None, request)
if not allow:
messages.error(self.request, _('You do not have permission to clone this event.'))
else:
@@ -242,6 +237,7 @@ class EventWizard(SafeSessionWizardView):
kwargs = {
'user': self.request.user,
'session': self.request.session,
'clone_from': self.clone_from,
}
if step != 'foundation':
fdata = self.get_cleaned_data_for_step('foundation')
@@ -253,6 +249,13 @@ class EventWizard(SafeSessionWizardView):
}
# The show must go on, we catch this error in render()
kwargs.update(fdata)
if step == 'copy':
bdata = self.get_cleaned_data_for_step('basics')
if bdata:
bdata = {
'team': bdata.get('team'),
}
kwargs.update(bdata)
return kwargs
def get_template_names(self):
@@ -277,18 +280,41 @@ class EventWizard(SafeSessionWizardView):
user=self.request.user,
)
if not EventWizardBasicsForm.has_control_rights(self.request.user, event.organizer, self.request.session):
if copy_data and copy_data['copy_from_event']:
copy_from_event = copy_data['copy_from_event']
elif self.clone_from:
copy_from_event = self.clone_from
else:
copy_from_event = None
if not EventWizardBasicsForm.has_control_rights(
self.request.user, event.organizer, self.request.session
):
if basics_data["team"] is not None:
t = basics_data["team"]
t.limit_events.add(event)
elif event.organizer.settings.event_team_provisioning:
# Create a new team for new events with full access, but for copied events with the same access
# as the source
limit_event_permissions = {}
if copy_from_event and copy_from_event.organizer == event.organizer:
source_teams = self.request.user._get_teams_for_event(copy_from_event.organizer, copy_from_event)
all_event_permissions = any(t.all_event_permissions for t in source_teams)
if not all_event_permissions:
for t in source_teams:
limit_event_permissions.update(t.limit_event_permissions)
else:
# The cross-organizer case is protected through allow_copy_data
all_event_permissions = True
t = Team.objects.create(
organizer=event.organizer,
name=_('Team {event}').format(
event=str(event.name)[:100] + "" if len(str(event.name)) > 100 else str(event.name)
),
all_organizer_permissions=False,
all_event_permissions=True,
all_event_permissions=all_event_permissions,
limit_event_permissions=limit_event_permissions,
)
t.members.add(self.request.user)
t.limit_events.add(event)
@@ -296,7 +322,8 @@ class EventWizard(SafeSessionWizardView):
'_created_by_event_wizard': True,
'name': t.name,
'all_organizer_permissions': False,
'all_event_permissions': True,
'all_event_permissions': all_event_permissions,
'limit_event_permissions': limit_event_permissions,
'limit_events': [event.pk],
})
@@ -307,11 +334,8 @@ class EventWizard(SafeSessionWizardView):
})
event.log_action('pretix.event.settings', user=self.request.user, data=logdata)
if copy_data and copy_data['copy_from_event']:
from_event = copy_data['copy_from_event']
event.copy_data_from(from_event)
elif self.clone_from:
event.copy_data_from(self.clone_from)
if copy_from_event:
event.copy_data_from(copy_from_event)
else:
event.set_active_plugins(settings.PRETIX_PLUGINS_DEFAULT.split(","),
allow_restricted=settings.PRETIX_PLUGINS_DEFAULT.split(","))
@@ -324,10 +348,8 @@ class EventWizard(SafeSessionWizardView):
event.set_defaults()
if basics_data['tax_rate'] is not None:
if self.clone_from:
default_tax_rule = self.clone_from.cached_default_tax_rule
elif copy_data and copy_data['copy_from_event']:
default_tax_rule = from_event.cached_default_tax_rule
if copy_from_event:
default_tax_rule = copy_from_event.cached_default_tax_rule
else:
default_tax_rule = None
if not default_tax_rule or default_tax_rule.rate != basics_data['tax_rate']:
@@ -341,7 +363,7 @@ class EventWizard(SafeSessionWizardView):
event.settings.set('locale', basics_data['locale'])
event.settings.set('locales', foundation_data['locales'])
if (copy_data and copy_data['copy_from_event']) or self.clone_from or event.has_subevents:
if copy_from_event or event.has_subevents:
return redirect(reverse('control:event.settings', kwargs={
'organizer': event.organizer.slug,
'event': event.slug,
+48 -1
View File
@@ -577,7 +577,9 @@ def test_event_create_with_clone_unknown_source(user, user_client, organizer, ev
@pytest.mark.django_db
def test_event_create_with_clone_across_organizers(user, user_client, organizer, event, taxrule):
def test_event_create_with_clone_across_organizers(user, user_client, organizer, event, taxrule, team):
team.all_event_permissions = True
team.save()
with scopes_disabled():
target_org = Organizer.objects.create(name='Dummy', slug='dummy2')
team = target_org.teams.create(
@@ -622,6 +624,51 @@ def test_event_create_with_clone_across_organizers(user, user_client, organizer,
assert cloned_event.tax_rules.exists()
@pytest.mark.django_db
def test_event_create_with_clone_across_organizers_lack_of_permission_on_source(user, user_client, team, organizer, event, taxrule):
team.all_event_permissions = False
team.limit_event_permissions = {
"event.settings.general:write": True,
}
team.save()
with scopes_disabled():
target_org = Organizer.objects.create(name='Dummy', slug='dummy2')
team = target_org.teams.create(
name="Test-Team",
all_event_permissions=True,
all_organizer_permissions=True,
)
team.members.add(user)
resp = user_client.post(
'/api/v1/organizers/{}/events/?clone_from={}/{}'.format(target_org.slug, organizer.slug, event.slug),
{
"name": {
"de": "Demo Konference 2020 Test",
"en": "Demo Conference 2020 Test"
},
"live": False,
"testmode": True,
"currency": "EUR",
"date_from": "2018-12-27T10:00:00Z",
"date_to": "2018-12-28T10:00:00Z",
"date_admission": None,
"is_public": False,
"presale_start": None,
"presale_end": None,
"location": None,
"slug": "2030",
"plugins": [
"pretix.plugins.ticketoutputpdf"
],
"timezone": "Europe/Vienna"
},
format='json'
)
assert resp.status_code == 403
assert resp.data["detail"] == "Not sufficient permission on source event to copy"
@pytest.mark.django_db
def test_event_put_with_clone(token_client, organizer, event, meta_prop):
resp = token_client.put(
+266 -2
View File
@@ -76,11 +76,16 @@ class EventsTest(SoupTest):
date_from=datetime.datetime(2014, 9, 5, tzinfo=datetime.timezone.utc),
)
self.team1 = Team.objects.create(organizer=self.orga1, all_event_permissions=True, limit_organizer_permissions={"organizer.events:create": 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, all_event_permissions=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')
@@ -1260,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()